# SPDX-License-Identifier: GPL-2.0-or-later
cmake_minimum_required(VERSION 3.21)
project(ps5c VERSION 0.1.0 LANGUAGES C)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8 OR NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^(AMD64|x86_64|amd64)$")
    message(FATAL_ERROR "PS5C targets x86-64 hosts only (found ${CMAKE_SYSTEM_PROCESSOR})")
endif()

option(PS5C_BUILD_TESTS "Build unit tests" ON)
option(PS5C_WERROR "Treat warnings as errors" OFF)
# Where launchers look for new builds (tools/serve_updates.ps1 on the local
# network today); [atualizacao] fonte in the user settings overrides it.
set(PS5C_UPDATE_URL "https://github.com/zeraps5/ZeraPS5-Releases/releases/latest/download" CACHE STRING "Update source built into the launcher")
set(PS5C_LOG_MIN_LEVEL "0" CACHE STRING "Compile out log calls below this level (0=trace .. 5=fatal)")

function(ps5c_target_defaults target)
    if(MSVC)
        target_compile_options(${target} PRIVATE /W4 /permissive- /utf-8)
        target_compile_definitions(${target} PRIVATE _CRT_SECURE_NO_WARNINGS)
        if(PS5C_WERROR)
            target_compile_options(${target} PRIVATE /WX)
        endif()
    else()
        target_compile_options(${target} PRIVATE -Wall -Wextra -Wpedantic -Wshadow
                                                 -Wstrict-prototypes -Wmissing-prototypes)
        target_compile_options(${target} PRIVATE
            "-ffile-prefix-map=${CMAKE_SOURCE_DIR}=/ZeraPS5"
            "-fdebug-prefix-map=${CMAKE_BINARY_DIR}=/ZeraPS5/build")
        if(PS5C_WERROR)
            target_compile_options(${target} PRIVATE -Werror)
        endif()
    endif()
    target_compile_definitions(${target} PRIVATE PS5C_LOG_MIN_LEVEL=${PS5C_LOG_MIN_LEVEL})
endfunction()

# Executables hosting guest memory: the guest owns 0x800000000-0x10000000000 at
# the same host address. High-entropy ASLR may place heaps/DLLs there before
# main() runs; keep host allocations low.
function(ps5c_guest_host target)
    if(WIN32)
        if(MSVC)
            target_link_options(${target} PRIVATE /HIGHENTROPYVA:NO)
        else()
            target_link_options(${target} PRIVATE -Wl,--disable-high-entropy-va)
        endif()
    endif()
endfunction()

configure_file(include/ps5c/version.h.in ${CMAKE_BINARY_DIR}/generated/ps5c/version.h @ONLY)
add_custom_target(ps5c_build_info
    COMMAND ${CMAKE_COMMAND} -DSRC=${CMAKE_SOURCE_DIR} -DOUT=${CMAKE_BINARY_DIR}/generated/ps5c/build_info.h
            -P ${CMAKE_SOURCE_DIR}/cmake/build_info.cmake
    BYPRODUCTS ${CMAKE_BINARY_DIR}/generated/ps5c/build_info.h ${CMAKE_BINARY_DIR}/generated/ps5c/shader_cache_version.h
    COMMENT "Build information (git commit, shader translator hash)")

# ---- base: portable C11, no OS calls ----
add_library(ps5c_base STATIC
    src/base/arena.c
    src/base/dynarray.c
    src/base/error.c
    src/base/hashmap.c
    src/base/ini.c
    src/base/json.c
    src/base/log.c
    src/base/log_record.c
    src/base/log_record_file.c
    src/base/log_session.c
    src/base/panic.c
    src/base/sha1.c
)
target_sources(ps5c_base PRIVATE src/base/log_record.c)
target_include_directories(ps5c_base PUBLIC src include ${CMAKE_BINARY_DIR}/generated)
target_include_directories(ps5c_base SYSTEM PUBLIC third_party/vulkan/include)
ps5c_target_defaults(ps5c_base)

# ---- pal: host operating system services ----
if(WIN32)
    add_library(ps5c_pal STATIC src/pal_win32/report_session.c src/pal_win32/report_archive.c src/pal/pal_exfat.c src/pal/pal_common.c src/pal/pal_media.c src/pal_win32/pal_win32.c
                src/pal_win32/pal_media_win32.c src/pal_win32/dualsense.c)
    target_link_libraries(ps5c_pal PUBLIC user32 gdi32 shell32 shlwapi ole32 mfuuid uuid synchronization winmm setupapi
                          hid)
else()
    add_library(ps5c_pal STATIC src/pal/pal_exfat.c src/pal/pal_common.c src/pal/pal_media.c src/pal_posix/pal_posix.c)
    find_package(Threads REQUIRED)
    target_link_libraries(ps5c_pal PUBLIC Threads::Threads)
    target_compile_definitions(ps5c_pal PRIVATE _GNU_SOURCE)
endif()
target_sources(ps5c_pal PRIVATE src/pal/module_paths.c)
target_link_libraries(ps5c_pal PUBLIC ps5c_base)
target_sources(ps5c_pal PRIVATE src/pal/module_paths.c)
ps5c_target_defaults(ps5c_pal)

# FFmpeg (movies and game audio): the static LGPL build tools/build_ffmpeg.sh
# makes in third_party/ffmpeg/<platform>. Without it movies fall back to Media
# Foundation on Windows and are skipped elsewhere.
if(WIN32)
    set(PS5C_FFMPEG_DIR ${CMAKE_SOURCE_DIR}/third_party/ffmpeg/win64 CACHE PATH "FFmpeg build (include/, lib/)")
else()
    set(PS5C_FFMPEG_DIR ${CMAKE_SOURCE_DIR}/third_party/ffmpeg/linux64 CACHE PATH "FFmpeg build (include/, lib/)")
endif()
if(EXISTS ${PS5C_FFMPEG_DIR}/lib/libavcodec.a)
    message(STATUS "FFmpeg: ${PS5C_FFMPEG_DIR}")
    target_sources(ps5c_pal PRIVATE src/pal/pal_media_ffmpeg.c src/pal/pal_vdec_ffmpeg.c)
    target_compile_definitions(ps5c_pal PUBLIC PS5C_HAVE_FFMPEG)
    target_include_directories(ps5c_pal SYSTEM PUBLIC ${PS5C_FFMPEG_DIR}/include)
    foreach(lib avformat avcodec swscale swresample avutil)
        target_link_libraries(ps5c_pal PUBLIC ${PS5C_FFMPEG_DIR}/lib/lib${lib}.a)
    endforeach()
    if(WIN32)
        target_link_libraries(ps5c_pal PUBLIC bcrypt ole32 user32)
        if(NOT MSVC)
            # FFmpeg's threads use winpthread: link it in, testers have no libwinpthread-1.dll
            target_link_libraries(ps5c_pal PUBLIC -Wl,-Bstatic winpthread -Wl,-Bdynamic)
        endif()
    else()
        target_link_libraries(ps5c_pal PUBLIC m)
    endif()
else()
    message(STATUS "FFmpeg: not built (tools/build_ffmpeg.sh); movies use the host decoder if any")
endif()

# ---- core: emulation (loader, recorder, ...) ----
add_library(ps5c_core STATIC
    src/core/runtime.c
    src/core/settings.c
    src/core/game_patch.c
    src/core/user_config.c
    src/cpu/x64_native.c
    src/gpu/gpu_cp.c
    src/gpu/rdna2_decode.c
    src/gpu/shader/shader_analyze.c
    src/gpu/shader/shader_dynamic.c
    src/gpu/shader/shader_corpus.c
    src/gpu/gpu_sampler.c
    src/gpu/shader/shader_spirv.c
    src/gpu/shader/spv.c
    src/gpu/vk/vk_api.c
    src/gpu/vk/vk_pipeline.c
    src/gpu/vk/vk_pipeline_async.c
    src/gpu/vk/vk_renderer.c
    src/gpu/vk/vk_shader_cache.c
    src/gpu/vk/vk_surface.c
    src/hle/hle.c
    src/hle/equeue.c
    src/hle/guest_printf.c
    src/hle/lib_agc_driver.c
    src/hle/audio_mix.c
    src/hle/lib_audio_out.c
    src/hle/lib_audio_out2.c
    src/hle/lib_av_player.c
    src/hle/lib_videodec2.c
    src/hle/lib_ampr.c
    src/hle/lib_png_enc.c
    src/hle/lib_ajm.c
    src/hle/lib_acm.c
    src/hle/lib_ngs2.c
    src/hle/lib_font.c
    src/hle/lib_voice.c
    src/hle/lib_np_trophy.c
    src/hle/lib_fiber.c
    src/hle/lib_net_offline.c
    src/hle/lib_ult.c
    src/hle/lib_platform.c
    src/hle/lib_json2.c
    src/hle/lib_rtc.c
    src/hle/addcont.c
    src/hle/lib_playgo.c
    src/hle/playgo_metadata.c
    src/hle/lib_save_data.c
    src/hle/lib_save_data_dir.c
    src/hle/lib_dialogs.c
    src/hle/lib_kernel.c
    src/hle/kernel_objects.c
    src/hle/lib_kernel_apr.c
    src/hle/lib_kernel_fs.c
    src/hle/net_loopback.c
    src/hle/lib_kernel_memory.c
    src/hle/lib_kernel_sync.c
    src/hle/lib_kernel_thread.c
    src/hle/lib_libc_internal.c
    src/hle/lib_services.c
    src/hle/lib_system.c
    src/hle/lib_video_out.c
    src/loader/linker.c
    src/loader/module_set.c
    src/loader/nid.c
    src/loader/nid_db.c
    src/loader/sce_image.c
    src/mem/gmem.c
    src/recorder/profile.c
)
target_sources(ps5c_core PRIVATE src/hle/net_loopback.c)
target_link_libraries(ps5c_core PUBLIC ps5c_pal)
ps5c_target_defaults(ps5c_core)
add_dependencies(ps5c_core ps5c_build_info) # shader_cache_version.h

# ---- command-line frontend ----
add_executable(ps5c frontends/cli/main.c)
target_link_libraries(ps5c PRIVATE ps5c_core)
add_dependencies(ps5c ps5c_build_info)
ps5c_target_defaults(ps5c)
ps5c_guest_host(ps5c)

# ---- launcher (Windows): ZeraPS5.exe next to ps5c.exe ----
if(WIN32)
    enable_language(RC)
    add_executable(zeraps5 WIN32 frontends/launcher/launcher.c frontends/launcher/launcher.rc src/core/user_config.c)
    set_target_properties(zeraps5 PROPERTIES OUTPUT_NAME ZeraPS5)
    target_include_directories(zeraps5 PRIVATE src ${CMAKE_BINARY_DIR}/generated)
    target_compile_definitions(zeraps5 PRIVATE UNICODE _UNICODE)
    target_link_libraries(zeraps5 PRIVATE ps5c_pal comctl32 gdiplus shell32 advapi32 ole32 winhttp bcrypt setupapi hid winmm)
    if(NOT MSVC)
        target_link_options(zeraps5 PRIVATE -municode)
    endif()
    target_compile_definitions(zeraps5 PRIVATE PS5C_UPDATE_URL="${PS5C_UPDATE_URL}")
    add_dependencies(zeraps5 ps5c_build_info)
    ps5c_target_defaults(zeraps5)
endif()

# NID name database next to the binary (see data/README.md)
if(EXISTS ${CMAKE_SOURCE_DIR}/data/nid_names.txt)
    configure_file(data/nid_names.txt ${CMAKE_BINARY_DIR}/data/nid_names.txt COPYONLY)
endif()

if(PS5C_BUILD_TESTS)
    enable_testing()
    add_executable(test_module_paths tests/unit/test_module_paths.c)
    target_link_libraries(test_module_paths PRIVATE ps5c_pal)
    ps5c_target_defaults(test_module_paths)
    file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/fixtures/module-paths")
    add_test(NAME module_paths COMMAND test_module_paths "${CMAKE_CURRENT_BINARY_DIR}/fixtures/module-paths")
    add_executable(test_log_record tests/unit/test_log_record.c)
    target_link_libraries(test_log_record PRIVATE ps5c_base)
    ps5c_target_defaults(test_log_record)
    add_test(NAME log_record COMMAND test_log_record)
    if(WIN32)
        add_executable(test_launcher_update tests/unit/test_launcher_update.c src/core/user_config.c)
        add_executable(test_log_record_file tests/unit/test_log_record_file.c)
        target_link_libraries(test_log_record_file PRIVATE ps5c_pal)
        if(NOT MSVC)
            target_link_options(test_log_record_file PRIVATE -municode)
        endif()
        ps5c_target_defaults(test_log_record_file)
        file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/fixtures/record-file")
        add_test(NAME log_record_file COMMAND test_log_record_file "${CMAKE_CURRENT_BINARY_DIR}/fixtures/record-file")
        target_include_directories(test_launcher_update PRIVATE src ${CMAKE_BINARY_DIR}/generated)
        target_compile_definitions(test_launcher_update PRIVATE UNICODE _UNICODE PS5C_UPDATE_URL="${PS5C_UPDATE_URL}")
        target_link_libraries(test_launcher_update PRIVATE ps5c_pal comctl32 gdiplus shell32 advapi32 ole32 winhttp bcrypt setupapi hid winmm)
        if(NOT MSVC)
            target_link_options(test_launcher_update PRIVATE -municode)
        endif()
        add_dependencies(test_launcher_update ps5c_build_info)
        add_test(NAME launcher_privacy COMMAND test_launcher_update)
        add_executable(test_launcher_reports tests/unit/test_launcher_reports.c src/core/user_config.c)
        target_include_directories(test_launcher_reports PRIVATE src ${CMAKE_BINARY_DIR}/generated)
        target_compile_definitions(test_launcher_reports PRIVATE UNICODE _UNICODE PS5C_UPDATE_URL="${PS5C_UPDATE_URL}")
        target_link_libraries(test_launcher_reports PRIVATE ps5c_pal comctl32 gdiplus shell32 advapi32 ole32 winhttp bcrypt setupapi hid winmm)
        if(NOT MSVC)
            target_link_options(test_launcher_reports PRIVATE -municode)
        endif()
        add_dependencies(test_launcher_reports ps5c_build_info)
        ps5c_target_defaults(test_launcher_reports)
        add_executable(test_launcher_quality tests/unit/test_launcher_quality.c src/core/user_config.c)
        target_include_directories(test_launcher_quality PRIVATE src ${CMAKE_BINARY_DIR}/generated)
        target_compile_definitions(test_launcher_quality PRIVATE UNICODE _UNICODE PS5C_UPDATE_URL="")
        target_link_libraries(test_launcher_quality PRIVATE ps5c_pal comctl32 gdiplus shell32 advapi32 ole32 winhttp bcrypt setupapi hid winmm)
        if(NOT MSVC)
            target_link_options(test_launcher_quality PRIVATE -municode)
        endif()
        add_dependencies(test_launcher_quality ps5c_build_info)
        ps5c_target_defaults(test_launcher_quality)
        file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/fixtures/report-quality")
        add_test(NAME launcher_report_quality COMMAND test_launcher_quality "${CMAKE_CURRENT_BINARY_DIR}/fixtures/report-quality")
        add_executable(test_launcher_sessions tests/unit/test_launcher_sessions.c src/core/user_config.c)
        target_include_directories(test_launcher_sessions PRIVATE src ${CMAKE_BINARY_DIR}/generated)
        target_compile_definitions(test_launcher_sessions PRIVATE UNICODE _UNICODE PS5C_UPDATE_URL="")
        target_link_libraries(test_launcher_sessions PRIVATE ps5c_pal comctl32 gdiplus shell32 advapi32 ole32 winhttp bcrypt setupapi hid winmm)
        if(NOT MSVC)
            target_link_options(test_launcher_sessions PRIVATE -municode)
        endif()
        add_dependencies(test_launcher_sessions ps5c_build_info)
        ps5c_target_defaults(test_launcher_sessions)
        file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/fixtures/c46")
        add_test(NAME launcher_sessions COMMAND test_launcher_sessions "${CMAKE_CURRENT_BINARY_DIR}/fixtures/c46")

        file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/fixtures/ui-languages")
        add_test(NAME launcher_localization COMMAND test_launcher_update language-tests "${CMAKE_CURRENT_BINARY_DIR}/fixtures/ui-languages")
    endif()
    add_executable(test_shader_global tests/unit/test_shader_global.c
                   src/gpu/shader/shader_analyze.c src/gpu/rdna2_decode.c)
    target_link_libraries(test_shader_global PRIVATE ps5c_base)
    ps5c_target_defaults(test_shader_global)
    add_test(NAME shader_global COMMAND test_shader_global)
    add_executable(test_texture_hash tests/unit/test_texture_hash.c)
    target_include_directories(test_texture_hash PRIVATE src)
    ps5c_target_defaults(test_texture_hash)
    add_test(NAME texture_hash COMMAND test_texture_hash)
    add_library(ps5c_test_support STATIC tests/support/sce_builder.c)
    target_include_directories(ps5c_test_support PUBLIC tests)
    target_link_libraries(ps5c_test_support PUBLIC ps5c_core)
    ps5c_target_defaults(ps5c_test_support)

    foreach(t avplayer_timing base pal loader mem exfat vertex_ranges texture_layout texture_tiling texture_3d image_views color_target dcc_sample fill_clear htile_clear shader_resinfo shader_corpus shader_gaps shader_inputs shader_lod shader_mip shader_half shader_half_sdwa shader_masks shader_dpp8 shader_gather_compare compute_dispatch buffer_pairs shader_resources store_range sbuffer audio_out ajm_resample agc_suspend agc_queues gpu_waits kernel_memory resource_limits runtime_faults thread_start playgo apr_events net_loopback acm snapshot_refresh shader_fp16_overflow shader_isa shader_isa_coverage json2 rtc stencil_state overlay)
        add_executable(test_${t} tests/unit/test_${t}.c)
        target_link_libraries(test_${t} PRIVATE ps5c_test_support)
        ps5c_target_defaults(test_${t})
        ps5c_guest_host(test_${t})
        add_test(NAME ${t} COMMAND test_${t})
    endforeach()
    add_executable(test_avplayer_policy tests/unit/test_avplayer_policy.c)
    target_link_libraries(test_avplayer_policy PRIVATE ps5c_test_support)
    ps5c_target_defaults(test_avplayer_policy)
    ps5c_guest_host(test_avplayer_policy)
    add_test(NAME avplayer_policy COMMAND test_avplayer_policy)
    add_executable(test_mem_pins tests/unit/test_mem_pins.c)
    target_link_libraries(test_mem_pins PRIVATE ps5c_test_support)
    ps5c_target_defaults(test_mem_pins)
    ps5c_guest_host(test_mem_pins)
    add_test(NAME mem_pins COMMAND test_mem_pins)
    set_tests_properties(mem_pins PROPERTIES TIMEOUT 15)
    if(WIN32)
        add_executable(test_mem_demand tests/unit/test_mem_demand.c)
        target_link_libraries(test_mem_demand PRIVATE ps5c_test_support)
        ps5c_target_defaults(test_mem_demand)
        ps5c_guest_host(test_mem_demand)
        add_test(NAME mem_demand COMMAND test_mem_demand)
        set_tests_properties(mem_demand PROPERTIES TIMEOUT 20)
    endif()
    add_executable(test_dynamic_buffers tests/unit/test_dynamic_buffers.c)
    target_link_libraries(test_dynamic_buffers PRIVATE ps5c_test_support)
    ps5c_target_defaults(test_dynamic_buffers)
    ps5c_guest_host(test_dynamic_buffers)
    add_test(NAME dynamic_buffers COMMAND test_dynamic_buffers)
    add_executable(test_dynamic_plan tests/unit/test_dynamic_plan.c)
    target_link_libraries(test_dynamic_plan PRIVATE ps5c_test_support)
    ps5c_target_defaults(test_dynamic_plan)
    ps5c_guest_host(test_dynamic_plan)
    add_test(NAME dynamic_plan COMMAND test_dynamic_plan)
    add_executable(test_shader_bvh tests/unit/test_shader_bvh.c)
    target_link_libraries(test_shader_bvh PRIVATE ps5c_test_support)
    ps5c_target_defaults(test_shader_bvh)
    ps5c_guest_host(test_shader_bvh)
    add_test(NAME shader_bvh COMMAND test_shader_bvh)
    add_executable(test_ghost_gpu tests/unit/test_ghost_gpu.c)
    target_link_libraries(test_ghost_gpu PRIVATE ps5c_test_support)
    ps5c_target_defaults(test_ghost_gpu)
    ps5c_guest_host(test_ghost_gpu)
    add_test(NAME ghost_gpu COMMAND test_ghost_gpu)
    add_executable(test_shader_link tests/unit/test_shader_link.c)
    target_link_libraries(test_shader_link PRIVATE ps5c_test_support)
    ps5c_target_defaults(test_shader_link)
    ps5c_guest_host(test_shader_link)
    add_test(NAME shader_link COMMAND test_shader_link)
    add_executable(test_lds64 tests/unit/test_lds64.c)
    target_link_libraries(test_lds64 PRIVATE ps5c_test_support)
    ps5c_target_defaults(test_lds64)
    ps5c_guest_host(test_lds64)
    add_test(NAME lds64 COMMAND test_lds64)
    add_executable(test_lod_stats tests/unit/test_lod_stats.c)
    target_link_libraries(test_lod_stats PRIVATE ps5c_test_support)
    ps5c_target_defaults(test_lod_stats)
    ps5c_guest_host(test_lod_stats)
    add_test(NAME lod_stats COMMAND test_lod_stats)
    add_executable(test_mesh_native tests/unit/test_mesh_native.c)
    target_link_libraries(test_mesh_native PRIVATE ps5c_test_support)
    ps5c_target_defaults(test_mesh_native)
    ps5c_guest_host(test_mesh_native)
    add_test(NAME mesh_native COMMAND test_mesh_native)
    add_executable(test_shader_waterfall tests/unit/test_shader_waterfall.c)
    target_link_libraries(test_shader_waterfall PRIVATE ps5c_test_support)
    ps5c_target_defaults(test_shader_waterfall)
    ps5c_guest_host(test_shader_waterfall)
    add_test(NAME shader_waterfall COMMAND test_shader_waterfall)
    add_executable(test_pixel_interlock tests/unit/test_pixel_interlock.c)
    target_link_libraries(test_pixel_interlock PRIVATE ps5c_test_support)
    ps5c_target_defaults(test_pixel_interlock)
    ps5c_guest_host(test_pixel_interlock)
    add_test(NAME pixel_interlock COMMAND test_pixel_interlock)
    set_tests_properties(agc_suspend PROPERTIES TIMEOUT 15 ENVIRONMENT "PS5C_WAIT_SUSPEND_POINT=1")
    add_test(NAME agc_suspend_resumable COMMAND test_agc_suspend)
    set_tests_properties(agc_suspend_resumable agc_queues PROPERTIES TIMEOUT 15 ENVIRONMENT "PS5C_WAIT_SUSPEND_POINT=1;PS5C_CP_WAITS=1")
    # Exercise the real cross-queue dependency with automatic GTA selection,
    # independently of the explicit diagnostic override tested above.
    file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/fixtures/agc-gta/sce_sys")
    file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/fixtures/agc-gta/sce_sys/param.json" "{\"titleId\":\"PPSA04263\"}\n")
    add_test(NAME agc_queues_gta_default COMMAND test_agc_queues "${CMAKE_CURRENT_BINARY_DIR}/fixtures/agc-gta")
    set_tests_properties(agc_queues_gta_default PROPERTIES TIMEOUT 15 ENVIRONMENT "PS5C_WAIT_SUSPEND_POINT=1;PS5C_CP_WAITS=")
    if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang" AND NOT MSVC AND NOT APPLE)
        add_executable(test_mem_remap tests/unit/test_mem_remap.c)
        target_link_libraries(test_mem_remap PRIVATE ps5c_test_support)
        target_link_options(test_mem_remap PRIVATE -Wl,--wrap=malloc -Wl,--wrap=calloc)
        ps5c_target_defaults(test_mem_remap)
        ps5c_guest_host(test_mem_remap)
        add_test(NAME mem_remap COMMAND test_mem_remap)
        add_test(NAME mem_remap_low_shared COMMAND test_mem_remap)
        set_tests_properties(mem_remap_low_shared PROPERTIES ENVIRONMENT "PS5C_LOW_SHARED_AREA=1")
    endif()
    add_test(NAME cli_host_info COMMAND ps5c --host-info)

    # End to end: synthetic game -> `ps5c analyze` (finds sce_module/, loads the NID database)
    add_executable(mksample tools/mksample.c)
    target_link_libraries(mksample PRIVATE ps5c_test_support)
    ps5c_target_defaults(mksample)
    file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/sample/sce_module)
    add_test(NAME sample_create COMMAND mksample ${CMAKE_BINARY_DIR}/sample)
    add_test(NAME cli_analyze
             COMMAND ps5c analyze ${CMAKE_BINARY_DIR}/sample/eboot.bin -o ${CMAKE_BINARY_DIR}/sample/game_profile.json)
    set_tests_properties(sample_create PROPERTIES FIXTURES_SETUP sample)
    set_tests_properties(cli_analyze PROPERTIES FIXTURES_REQUIRED sample
                         WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
                         PASS_REGULAR_EXPRESSION "unique imports : 5 \\(5 with known names\\).*0 HLE, 1 LLE, 4 missing")
    add_test(NAME cli_load COMMAND ps5c load ${CMAKE_BINARY_DIR}/sample/eboot.bin)
    set_tests_properties(cli_load PROPERTIES FIXTURES_REQUIRED sample
                         PASS_REGULAR_EXPRESSION "modules mapped      : 2.*relocations skipped : 0")
endif()
