set(BADMAN_ELF "${CMAKE_CURRENT_SOURCE_DIR}/game/PSP_GAME/SYSDIR/EBOOT_DECRYPTED.ELF"
    CACHE FILEPATH "Verified ULUS10456 decrypted executable")
option(BADMAN_BUILD_NATIVE "Build the diagnostic Badman native host" ON)
option(BADMAN_ENABLE_GPU_TESTS "Run hardware D3D11 and Vulkan reference comparisons" OFF)
if(PSPRECOMP_AOT_PRODUCTION_FASTPATHS)
    message(FATAL_ERROR "Badman needs AOT chain hooks for safe font module stop/unload; set PSPRECOMP_AOT_PRODUCTION_FASTPATHS=OFF")
endif()
# The native preparation tool embeds config/ facts generated by Python.
find_package(Python3 3.11 REQUIRED COMPONENTS Interpreter)
set(BADMAN_FACTS_HEADER "${CMAKE_CURRENT_BINARY_DIR}/facts/badman_profile_facts.hpp")
add_custom_command(OUTPUT "${BADMAN_FACTS_HEADER}"
    COMMAND "${Python3_EXECUTABLE}" -X utf8 "${CMAKE_CURRENT_SOURCE_DIR}/tools/embed_profile_facts.py" --output "${BADMAN_FACTS_HEADER}"
    DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/tools/embed_profile_facts.py"
        "${CMAKE_CURRENT_SOURCE_DIR}/config/executable.json" "${CMAKE_CURRENT_SOURCE_DIR}/config/font_module.json"
        "${CMAKE_CURRENT_SOURCE_DIR}/config/font_relocations.json" "${CMAKE_CURRENT_SOURCE_DIR}/config/code_ranges.csv"
        "${CMAKE_CURRENT_SOURCE_DIR}/config/font_code_ranges.csv"
    COMMENT "Embedding Badman profile facts" VERBATIM)
# Python-free preparation (font module + partitioned generation) for Android.
add_executable(badman_prepare tools/native/badman_prepare.cpp "${BADMAN_FACTS_HEADER}")
target_include_directories(badman_prepare PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/facts")
target_link_libraries(badman_prepare PRIVATE psprecomp_core)
set_target_properties(badman_prepare PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/$<CONFIG>")

add_library(badman_host STATIC host/badman_host.cpp host/badman_sync.cpp host/badman_ge.cpp host/badman_ge_hle.cpp
    host/badman_display.cpp host/badman_disc.cpp host/badman_io.cpp host/badman_modules.cpp
    host/badman_registry.cpp host/badman_window.cpp host/badman_presenter.cpp host/badman_scene.cpp host/badman_scene_render.cpp host/badman_raster.cpp host/badman_input.cpp
    host/badman_texture.cpp host/badman_clut.cpp host/badman_audio.cpp host/badman_savedata.cpp
    host/badman_savedata_hle.cpp host/badman_thread_services.cpp host/badman_mpeg.cpp
    host/badman_mpeg_hle.cpp host/badman_video_ffmpeg.cpp host/badman_events.cpp
    host/badman_display_timing.cpp host/badman_dma.cpp host/badman_atrac.cpp host/badman_atrac_ffmpeg.cpp
    host/badman_atrac_hle.cpp host/badman_audio_hle.cpp host/badman_osk.cpp host/badman_osk_hle.cpp
    host/badman_gpu_raster.cpp host/badman_gpu_d3d11.cpp host/badman_gpu_vulkan.cpp host/badman_clock.cpp
    host/badman_screenshot.cpp host/badman_screenshot_hle.cpp host/badman_exit_services.cpp host/badman_self_exit.cpp
    host/badman_native_fs.cpp host/badman_startup.cpp host/badman_workers.cpp host/badman_gpu_cpu.cpp)
target_include_directories(badman_host PRIVATE "${CMAKE_SOURCE_DIR}/third_party/vulkan_headers/include")

set(BADMAN_FFMPEG_PREFIX "${CMAKE_SOURCE_DIR}/out/tools/ffmpeg-8.1-lgpl-edc7450333aa/ffmpeg-n8.1-latest-win64-lgpl-shared-8.1"
    CACHE PATH "Separately installed LGPL FFmpeg shared headers and runtime")
if(EXISTS "${BADMAN_FFMPEG_PREFIX}/include/libavcodec/avcodec.h")
    target_include_directories(badman_host PRIVATE "${BADMAN_FFMPEG_PREFIX}/include")
    target_compile_definitions(badman_host PRIVATE BADMAN_FFMPEG_HEADERS=1)
endif()

add_executable(BadmanMpegProbe host/mpeg_probe.cpp)
target_link_libraries(BadmanMpegProbe PRIVATE badman_host)
set_target_properties(BadmanMpegProbe PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/$<CONFIG>")
if(MSVC)
    target_sources(BadmanMpegProbe PRIVATE host/windows.manifest)
endif()
add_executable(BadmanAtracProbe host/atrac_probe.cpp)
target_link_libraries(BadmanAtracProbe PRIVATE badman_host)
set_target_properties(BadmanAtracProbe PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/$<CONFIG>")
if(MSVC)
    target_sources(BadmanAtracProbe PRIVATE host/windows.manifest)
endif()
target_link_libraries(badman_host PUBLIC psprecomp_core)
target_include_directories(badman_host PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/host")
if(WIN32)
    target_link_libraries(badman_host PUBLIC user32 gdi32 xaudio2 xinput ole32 windowscodecs d3d11 dxgi d3dcompiler)
endif()
if(ANDROID)
    target_sources(badman_host PRIVATE host/badman_android_bridge.cpp host/badman_profiler.cpp host/badman_android_presenter.cpp)
    target_link_libraries(badman_host PUBLIC aaudio log android EGL GLESv3)
endif()

# Identities every game host checks: the decrypted executable and font module.
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/config/executable.json" BADMAN_IDENTITY)
string(JSON BADMAN_EXPECTED_SHA256 GET "${BADMAN_IDENTITY}" elf_sha256)
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/config/font_module.json" BADMAN_FONT_IDENTITY)
string(JSON BADMAN_FONT_SHA256 GET "${BADMAN_FONT_IDENTITY}" prepared_sha256)
string(JSON BADMAN_FONT_SOURCE_SHA256 GET "${BADMAN_FONT_IDENTITY}" input_sha256)
# Android links the locally generated translation as its own library
# (libbadman_game.so) against one shared runtime (libbadman_runtime.so).
option(BADMAN_SPLIT_GAME_LIBRARY "Build the generated game as a separate shared library" ${ANDROID})
if(BADMAN_SPLIT_GAME_LIBRARY)
    add_library(badman_runtime SHARED host/runtime_library.cpp)
    target_link_libraries(badman_runtime PRIVATE "$<LINK_LIBRARY:WHOLE_ARCHIVE,badman_host,psprecomp_core>")
    target_include_directories(badman_runtime PUBLIC "${CMAKE_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/host")
    if(ANDROID)
        target_link_libraries(badman_runtime PUBLIC aaudio log dl android)
    endif()
    set_target_properties(badman_runtime PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/$<CONFIG>")
endif()
if(ANDROID AND BADMAN_SPLIT_GAME_LIBRARY)
    # The app's JNI library runs the same native main on a game thread. Like
    # the runtime it is built from sources only; the game library is built on
    # the phone from the player's own ISO.
    add_library(badman_android SHARED host/android_app.cpp host/main.cpp)
    target_link_libraries(badman_android PRIVATE badman_runtime android log)
    target_compile_definitions(badman_android PRIVATE BADMAN_EMBEDDED_MAIN=1 BADMAN_GAME_LIBRARY=1
        BADMAN_EXPECTED_ELF_SHA256="${BADMAN_EXPECTED_SHA256}" BADMAN_FONT_SHA256="${BADMAN_FONT_SHA256}"
        BADMAN_FONT_SOURCE_SHA256="${BADMAN_FONT_SOURCE_SHA256}" BADMAN_FONT_ELF_PATH="" BADMAN_FONT_DIRECTORY=""
        BADMAN_FFMPEG_DIRECTORY="")
    set_target_properties(badman_android PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/$<CONFIG>")
endif()

if(BADMAN_BUILD_NATIVE)
    set(BADMAN_FONT_ELF "${CMAKE_SOURCE_DIR}/out/badman/font/libfont-fixed.elf"
        CACHE FILEPATH "Verified externally relocated fixed-address font ELF")
    if(NOT EXISTS "${BADMAN_FONT_ELF}")
        message(FATAL_ERROR "Prepare the local font module with tools/prepare_font.py before building BadmanNative; use BADMAN_BUILD_NATIVE=OFF for host-only tests.")
    endif()
    if(NOT EXISTS "${BADMAN_ELF}")
        message(FATAL_ERROR "Badman needs its local decrypted ELF: ${BADMAN_ELF}. Use BADMAN_BUILD_NATIVE=OFF for host tests only.")
    endif()
    # Cross builds (for example Android) must generate with a recompiler that
    # runs on the build machine; the target's own psp_recomp cannot execute here.
    set(PSPRECOMP_HOST_RECOMPILER "" CACHE FILEPATH "Build-machine psp_recomp for cross compilation")
    if(PSPRECOMP_HOST_RECOMPILER)
        set(BADMAN_RECOMPILER "${PSPRECOMP_HOST_RECOMPILER}")
        set(BADMAN_RECOMPILER_DEPENDENCY "${PSPRECOMP_HOST_RECOMPILER}")
    elseif(CMAKE_CROSSCOMPILING)
        message(FATAL_ERROR "Cross-compiling BadmanNative requires PSPRECOMP_HOST_RECOMPILER")
    else()
        set(BADMAN_RECOMPILER "$<TARGET_FILE:psp_recomp>")
        set(BADMAN_RECOMPILER_DEPENDENCY psp_recomp)
    endif()
    set(BADMAN_GENERATED "${CMAKE_CURRENT_BINARY_DIR}/generated")
    set(BADMAN_GENERATOR "${CMAKE_CURRENT_SOURCE_DIR}/tools/generate.py")
    set(BADMAN_RANGES "${CMAKE_CURRENT_SOURCE_DIR}/config/code_ranges.csv")
    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${BADMAN_RANGES}")
    execute_process(COMMAND "${Python3_EXECUTABLE}" -X utf8 "${BADMAN_GENERATOR}"
        --list --output "${BADMAN_GENERATED}"
        OUTPUT_VARIABLE BADMAN_SOURCES OUTPUT_STRIP_TRAILING_WHITESPACE
        ENCODING UTF-8
        COMMAND_ERROR_IS_FATAL ANY)
    add_custom_command(OUTPUT ${BADMAN_SOURCES} "${BADMAN_GENERATED}/generation.json"
        COMMAND "${Python3_EXECUTABLE}" -X utf8 "${BADMAN_GENERATOR}"
            --elf "${BADMAN_ELF}" --recompiler "${BADMAN_RECOMPILER}"
            --output "${BADMAN_GENERATED}"
        DEPENDS ${BADMAN_RECOMPILER_DEPENDENCY} "${BADMAN_ELF}" "${BADMAN_GENERATOR}" "${BADMAN_RANGES}"
            "${CMAKE_CURRENT_SOURCE_DIR}/config/executable.json"
        COMMENT "Generating Badman AOT code from defined Allegrex instruction ranges"
        VERBATIM)
    if(BADMAN_SPLIT_GAME_LIBRARY)
        add_executable(BadmanNative host/main.cpp)
        target_link_libraries(BadmanNative PRIVATE badman_runtime)
        target_compile_definitions(BadmanNative PRIVATE BADMAN_GAME_LIBRARY=1)
        add_library(badman_game SHARED ${BADMAN_SOURCES} host/game_entry.cpp)
        target_link_libraries(badman_game PRIVATE badman_runtime)
        set_target_properties(badman_game PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/$<CONFIG>")
    else()
        add_executable(BadmanNative host/main.cpp ${BADMAN_SOURCES})
        target_link_libraries(BadmanNative PRIVATE badman_host)
    endif()
    target_compile_definitions(BadmanNative PRIVATE BADMAN_EXPECTED_ELF_SHA256="${BADMAN_EXPECTED_SHA256}")
    target_compile_definitions(BadmanNative PRIVATE BADMAN_FFMPEG_DIRECTORY="${BADMAN_FFMPEG_PREFIX}/bin")
    if(MSVC)
        set(BADMAN_GENERATED_MP "${PSPRECOMP_MSVC_MP_FLAG}")
        if(PSPRECOMP_MSVC_MP_JOBS STREQUAL "0")
            set(BADMAN_GENERATED_MP "/MP2")
        endif()
        # Optimize the main Allegrex translation separately from font-module
        # bring-up, retaining precise floating-point operations.
        set_source_files_properties(${BADMAN_SOURCES} PROPERTIES
            COMPILE_OPTIONS "/O2;/Ob2;/fp:precise;/bigobj;${BADMAN_GENERATED_MP}")
        target_link_options(BadmanNative PRIVATE /STACK:8388608)
    else()
        # Match MSVC /fp:precise: no FMA contraction (ARM64 Clang contracts by
        # default) and no type-based alias assumptions across guest memory views.
        set_source_files_properties(${BADMAN_SOURCES} PROPERTIES COMPILE_OPTIONS
            "-O2;-g0;-ffp-contract=off;-fno-strict-aliasing")
    endif()
    set_target_properties(BadmanNative PROPERTIES
        RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/$<CONFIG>")

    set(BADMAN_FONT_GENERATED "${CMAKE_CURRENT_BINARY_DIR}/font_generated")
    set(BADMAN_FONT_GENERATOR "${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_font.py")
    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
        "${CMAKE_CURRENT_SOURCE_DIR}/config/font_module.json"
        "${CMAKE_CURRENT_SOURCE_DIR}/config/font_code_ranges.csv")
    execute_process(COMMAND "${Python3_EXECUTABLE}" -X utf8 "${BADMAN_FONT_GENERATOR}"
        --list --output "${BADMAN_FONT_GENERATED}" --allowed-output-root "${CMAKE_BINARY_DIR}"
        OUTPUT_VARIABLE BADMAN_FONT_SOURCES OUTPUT_STRIP_TRAILING_WHITESPACE
        ENCODING UTF-8
        COMMAND_ERROR_IS_FATAL ANY)
    add_custom_command(OUTPUT ${BADMAN_FONT_SOURCES} "${BADMAN_FONT_GENERATED}/font_generation.json"
        COMMAND "${Python3_EXECUTABLE}" -X utf8 "${BADMAN_FONT_GENERATOR}"
            --elf "${BADMAN_FONT_ELF}" --recompiler "${BADMAN_RECOMPILER}"
            --output "${BADMAN_FONT_GENERATED}" --allowed-output-root "${CMAKE_BINARY_DIR}"
        DEPENDS ${BADMAN_RECOMPILER_DEPENDENCY} "${BADMAN_FONT_ELF}" "${BADMAN_FONT_GENERATOR}"
            "${CMAKE_CURRENT_SOURCE_DIR}/config/font_module.json"
            "${CMAKE_CURRENT_SOURCE_DIR}/config/font_code_ranges.csv"
        COMMENT "Generating the separately relocated font module" VERBATIM)
    add_library(badman_font_aot STATIC ${BADMAN_FONT_SOURCES})
    target_link_libraries(badman_font_aot PUBLIC psprecomp_core)
    if(MSVC)
        target_compile_options(badman_font_aot PRIVATE /Od /Ob0 /bigobj /MP2)
    else()
        target_compile_options(badman_font_aot PRIVATE -O0 -g0)
    endif()
    if(BADMAN_SPLIT_GAME_LIBRARY)
        target_sources(badman_game PRIVATE ${BADMAN_FONT_SOURCES})
        set_source_files_properties(${BADMAN_FONT_SOURCES} TARGET_DIRECTORY badman_game PROPERTIES COMPILE_OPTIONS "-O0;-g0")
    else()
        target_link_libraries(BadmanNative PRIVATE badman_font_aot)
    endif()
    set(BADMAN_FONT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/analysis/firmware/ppsspp-replacements/flash0/font"
        CACHE PATH "Local PGF directory, mounted read-only as flash0:/font")
    # Per target, not per source: the Android app compiles main.cpp with its own values.
    target_compile_definitions(BadmanNative PRIVATE BADMAN_FONT_ELF_PATH="${BADMAN_FONT_ELF}"
        BADMAN_FONT_SHA256="${BADMAN_FONT_SHA256}" BADMAN_FONT_SOURCE_SHA256="${BADMAN_FONT_SOURCE_SHA256}"
        BADMAN_FONT_DIRECTORY="${BADMAN_FONT_DIRECTORY}")
    add_executable(BadmanFontProbe host/font_probe.cpp)
    target_link_libraries(BadmanFontProbe PRIVATE badman_host badman_font_aot)
    target_compile_definitions(BadmanFontProbe PRIVATE
        BADMAN_FONT_SHA256="${BADMAN_FONT_SHA256}"
        BADMAN_FONT_SOURCE_SHA256="${BADMAN_FONT_SOURCE_SHA256}")
    set_target_properties(BadmanFontProbe PROPERTIES
        RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/$<CONFIG>")
    if(MSVC)
        target_sources(BadmanNative PRIVATE host/windows.manifest host/badman.rc)
        target_sources(BadmanFontProbe PRIVATE host/windows.manifest)
    endif()
endif()

if(WIN32)
    option(BADMAN_BUILD_LAUNCHER "Build the game-independent own-ISO recompilation launcher" ON)
    if(BADMAN_BUILD_LAUNCHER)
        # The launcher hosts launcher-ui/ in WebView2 (BSD-licensed SDK, static loader).
        set(BADMAN_WEBVIEW2_SDK "${CMAKE_SOURCE_DIR}/out/tools/webview2-1.0.2903.40/pkg/build/native"
            CACHE PATH "Microsoft.Web.WebView2 1.0.2903.40 native SDK (include, x64)")
        if(NOT EXISTS "${BADMAN_WEBVIEW2_SDK}/include/WebView2.h")
            message(FATAL_ERROR "BadmanLauncher needs the WebView2 SDK at BADMAN_WEBVIEW2_SDK (or set BADMAN_BUILD_LAUNCHER=OFF)")
        endif()
        add_executable(BadmanLauncher WIN32 host/launcher.cpp)
        target_compile_definitions(BadmanLauncher PRIVATE UNICODE _UNICODE NOMINMAX WIN32_LEAN_AND_MEAN
            BADMAN_LAUNCHER_UI_SOURCE="${CMAKE_CURRENT_SOURCE_DIR}/launcher-ui")
        target_include_directories(BadmanLauncher PRIVATE "${BADMAN_WEBVIEW2_SDK}/include")
        target_link_libraries(BadmanLauncher PRIVATE "${BADMAN_WEBVIEW2_SDK}/x64/WebView2LoaderStatic.lib"
            shell32 shlwapi ole32 oleaut32 advapi32 version comdlg32 uuid)
        if(MSVC)
            target_sources(BadmanLauncher PRIVATE host/windows.manifest host/badman.rc)
            set_property(TARGET BadmanLauncher PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
        endif()
        set_target_properties(BadmanLauncher PROPERTIES
            RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/$<CONFIG>")
    endif()
endif()

if(PSPRECOMP_BUILD_PROFILE_TESTS)
    enable_testing()
    if(WIN32)
        add_executable(badman_capture_file_tests tests/capture_file_tests.cpp)
        target_include_directories(badman_capture_file_tests PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/host")
        add_test(NAME badman_capture_file_tests COMMAND badman_capture_file_tests)
        set_tests_properties(badman_capture_file_tests PROPERTIES TIMEOUT 15)
    endif()
    add_executable(badman_host_tests tests/host_tests.cpp)
    target_link_libraries(badman_host_tests PRIVATE badman_host)
    add_test(NAME badman_host_tests COMMAND badman_host_tests)
    add_executable(badman_sync_tests tests/sync_tests.cpp)
    target_link_libraries(badman_sync_tests PRIVATE badman_host)
    add_test(NAME badman_sync_tests COMMAND badman_sync_tests)
    add_executable(badman_audio_tests tests/audio_tests.cpp)
    target_link_libraries(badman_audio_tests PRIVATE badman_host)
    add_test(NAME badman_audio_tests COMMAND badman_audio_tests)
    add_executable(badman_savedata_tests tests/savedata_tests.cpp)
    target_link_libraries(badman_savedata_tests PRIVATE badman_host)
    add_test(NAME badman_savedata_tests COMMAND badman_savedata_tests)
    add_executable(badman_startup_tests tests/startup_tests.cpp)
    target_link_libraries(badman_startup_tests PRIVATE badman_host)
    add_test(NAME badman_startup_tests COMMAND badman_startup_tests)
    add_executable(badman_osk_tests tests/osk_tests.cpp)
    target_link_libraries(badman_osk_tests PRIVATE badman_host)
    add_test(NAME badman_osk_tests COMMAND badman_osk_tests)
    add_executable(badman_screenshot_tests tests/screenshot_tests.cpp)
    target_link_libraries(badman_screenshot_tests PRIVATE badman_host)
    add_test(NAME badman_screenshot_tests COMMAND badman_screenshot_tests)
    add_executable(badman_thread_service_tests tests/thread_service_tests.cpp)
    target_link_libraries(badman_thread_service_tests PRIVATE badman_host)
    add_test(NAME badman_thread_service_tests COMMAND badman_thread_service_tests)
    add_executable(badman_mpeg_tests tests/mpeg_tests.cpp)
    target_link_libraries(badman_mpeg_tests PRIVATE badman_host)
    add_test(NAME badman_mpeg_tests COMMAND badman_mpeg_tests)
    add_executable(badman_mpeg_callback_tests tests/mpeg_callback_tests.cpp)
    target_link_libraries(badman_mpeg_callback_tests PRIVATE badman_host)
    add_test(NAME badman_mpeg_callback_tests COMMAND badman_mpeg_callback_tests)
    add_executable(badman_event_tests tests/event_tests.cpp)
    target_link_libraries(badman_event_tests PRIVATE badman_host)
    add_test(NAME badman_event_tests COMMAND badman_event_tests)
    add_executable(badman_display_timing_tests tests/display_timing_tests.cpp)
    target_link_libraries(badman_display_timing_tests PRIVATE badman_host)
    add_test(NAME badman_display_timing_tests COMMAND badman_display_timing_tests)
    add_executable(badman_dma_tests tests/dma_tests.cpp)
    target_link_libraries(badman_dma_tests PRIVATE badman_host)
    add_test(NAME badman_dma_tests COMMAND badman_dma_tests)
    add_executable(badman_atrac_tests tests/atrac_tests.cpp)
    target_link_libraries(badman_atrac_tests PRIVATE badman_host)
    add_test(NAME badman_atrac_tests COMMAND badman_atrac_tests)
    add_executable(badman_atrac_hle_tests tests/atrac_hle_tests.cpp)
    target_link_libraries(badman_atrac_hle_tests PRIVATE badman_host)
    add_test(NAME badman_atrac_hle_tests COMMAND badman_atrac_hle_tests)
    add_executable(badman_audio_hle_tests tests/audio_hle_tests.cpp)
    target_link_libraries(badman_audio_hle_tests PRIVATE badman_host)
    add_test(NAME badman_audio_hle_tests COMMAND badman_audio_hle_tests)
    add_executable(badman_ge_tests tests/ge_tests.cpp)
    target_link_libraries(badman_ge_tests PRIVATE badman_host)
    add_test(NAME badman_ge_tests COMMAND badman_ge_tests)
    add_executable(badman_disc_tests tests/disc_tests.cpp)
    target_link_libraries(badman_disc_tests PRIVATE badman_host)
    add_test(NAME badman_disc_tests COMMAND badman_disc_tests)
    add_executable(badman_module_tests tests/module_tests.cpp)
    target_link_libraries(badman_module_tests PRIVATE badman_host)
    add_test(NAME badman_module_tests COMMAND badman_module_tests)
    add_executable(badman_exit_services_tests tests/exit_services_tests.cpp)
    target_link_libraries(badman_exit_services_tests PRIVATE badman_host)
    add_test(NAME badman_exit_services_tests COMMAND badman_exit_services_tests)
    add_executable(badman_self_exit_tests tests/self_exit_tests.cpp)
    target_link_libraries(badman_self_exit_tests PRIVATE badman_host)
    add_test(NAME badman_self_exit_tests COMMAND badman_self_exit_tests)
    add_executable(badman_io_tests tests/io_tests.cpp)
    target_link_libraries(badman_io_tests PRIVATE badman_host)
    add_test(NAME badman_io_tests COMMAND badman_io_tests)
    if(WIN32)
        add_executable(badman_native_fs_tests tests/native_fs_tests.cpp)
        target_link_libraries(badman_native_fs_tests PRIVATE badman_host)
        add_test(NAME badman_native_fs_tests COMMAND badman_native_fs_tests)
    endif()
    add_executable(badman_registry_tests tests/registry_tests.cpp)
    target_link_libraries(badman_registry_tests PRIVATE badman_host)
    add_test(NAME badman_registry_tests COMMAND badman_registry_tests)
    add_executable(badman_scene_tests tests/scene_tests.cpp)
    target_link_libraries(badman_scene_tests PRIVATE badman_host)
    add_test(NAME badman_scene_tests COMMAND badman_scene_tests)
    add_executable(badman_window_tests tests/window_tests.cpp)
    target_link_libraries(badman_window_tests PRIVATE badman_host)
    add_test(NAME badman_window_tests COMMAND badman_window_tests)
    add_executable(badman_raster_tests tests/raster_tests.cpp)
    target_link_libraries(badman_raster_tests PRIVATE badman_host)
    add_test(NAME badman_raster_tests COMMAND badman_raster_tests)
    add_executable(badman_input_tests tests/input_tests.cpp)
    target_link_libraries(badman_input_tests PRIVATE badman_host)
    add_test(NAME badman_input_tests COMMAND badman_input_tests)
    add_executable(badman_texture_tests tests/texture_tests.cpp)
    target_link_libraries(badman_texture_tests PRIVATE badman_host)
    add_test(NAME badman_texture_tests COMMAND badman_texture_tests)
    add_executable(badman_clut_tests tests/clut_tests.cpp)
    target_link_libraries(badman_clut_tests PRIVATE badman_host)
    add_test(NAME badman_clut_tests COMMAND badman_clut_tests)
    # The CPU executor of the GPU pixel program needs no GPU: compare it with
    # the software rasterizer over full guest memory on every platform.
    add_executable(badman_cpu_raster_tests tests/gpu_raster_tests.cpp)
    target_link_libraries(badman_cpu_raster_tests PRIVATE badman_host)
    add_test(NAME badman_cpu_raster_tests COMMAND badman_cpu_raster_tests cpu)
    if(WIN32 AND BADMAN_ENABLE_GPU_TESTS)
        add_executable(badman_gpu_raster_tests tests/gpu_raster_tests.cpp)
        target_link_libraries(badman_gpu_raster_tests PRIVATE badman_host)
        add_test(NAME badman_gpu_raster_tests COMMAND badman_gpu_raster_tests)
        add_executable(badman_ge_break_gpu_tests tests/ge_break_gpu_tests.cpp)
        target_link_libraries(badman_ge_break_gpu_tests PRIVATE badman_host)
        add_test(NAME badman_ge_break_gpu_tests COMMAND badman_ge_break_gpu_tests)
    endif()
    add_test(NAME badman_codegen_tests COMMAND "${Python3_EXECUTABLE}"
        "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_codegen.py")
    add_test(NAME badman_setup_tests COMMAND "${Python3_EXECUTABLE}"
        "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_setup.py")
    add_test(NAME badman_font_relocation_tests COMMAND "${Python3_EXECUTABLE}"
        "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_font_relocation.py")
    add_test(NAME badman_package_tests COMMAND "${Python3_EXECUTABLE}"
        "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_package.py")
    add_test(NAME badman_local_build_tests COMMAND "${Python3_EXECUTABLE}"
        "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_build_game.py")
    add_test(NAME badman_bootstrap_tests COMMAND "${Python3_EXECUTABLE}"
        "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_toolchain_bootstrap.py")
endif()
