cmake_minimum_required(VERSION 3.20)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
project(OpenXML1xbox LANGUAGES C CXX RC)
add_executable(raven-flight-ceiling-test tests/engine/actors/raven_flight_ceiling_test.c)
target_include_directories(raven-flight-ceiling-test PRIVATE src)
find_package(Python3 3.10 REQUIRED COMPONENTS Interpreter)
include(cmake/Generated.cmake)
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/native-parser-text-methods.inc"
    COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/guard-parser-text-arena.py"
        --generated-dir "${XML1_RECOMP_SOURCE_DIR}"
        --fixture-output "${CMAKE_CURRENT_BINARY_DIR}/native-parser-text-methods.inc"
    DEPENDS scripts/guard-parser-text-arena.py ${XML1_GENERATED_INPUTS}
    VERBATIM)
add_executable(parser-text-arena-native-test tests/parser_text_arena_native_test.c
    "${CMAKE_CURRENT_BINARY_DIR}/native-parser-text-methods.inc")
target_include_directories(parser-text-arena-native-test PRIVATE src "${CMAKE_CURRENT_BINARY_DIR}")
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/native-registry-methods.inc"
    COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/guard-talent-registry.py"
        --generated-dir "${XML1_RECOMP_SOURCE_DIR}"
        --fixture-output "${CMAKE_CURRENT_BINARY_DIR}/native-registry-methods.inc"
    DEPENDS scripts/guard-talent-registry.py scripts/talent_registry_layout.py
        src/engine/progression/talent_registry_layout.h ${XML1_GENERATED_INPUTS}
    VERBATIM)
add_executable(talent-registry-native-test tests/engine/progression/talent_registry_native_test.c
    "${CMAKE_CURRENT_BINARY_DIR}/native-registry-methods.inc")
target_include_directories(talent-registry-native-test PRIVATE src "${CMAKE_CURRENT_BINARY_DIR}")
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/native-source-cache-methods.inc"
    COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/guard-talent-source-cache.py"
        --generated-dir "${XML1_RECOMP_SOURCE_DIR}"
        --fixture-output "${CMAKE_CURRENT_BINARY_DIR}/native-source-cache-methods.inc"
    DEPENDS scripts/guard-talent-source-cache.py scripts/talent_registry_layout.py
        ${XML1_GENERATED_INPUTS}
    VERBATIM)
add_executable(talent-source-cache-native-test tests/engine/progression/talent_source_cache_native_test.c
    "${CMAKE_CURRENT_BINARY_DIR}/native-source-cache-methods.inc")
target_include_directories(talent-source-cache-native-test PRIVATE src "${CMAKE_CURRENT_BINARY_DIR}")
add_executable(talent-slot-layout-test tests/engine/progression/talent_slot_layout_test.c)
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/native-source-method.inc"
    COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/guard-talent-sources.py"
        --generated-dir "${XML1_RECOMP_SOURCE_DIR}"
        --fixture-output "${CMAKE_CURRENT_BINARY_DIR}/native-source-method.inc"
    DEPENDS scripts/guard-talent-sources.py ${XML1_GENERATED_INPUTS}
    VERBATIM)
add_executable(talent-source-native-test tests/engine/progression/talent_source_native_test.c
    "${CMAKE_CURRENT_BINARY_DIR}/native-source-method.inc")
target_include_directories(talent-source-native-test PRIVATE src "${CMAKE_CURRENT_BINARY_DIR}")
target_include_directories(talent-slot-layout-test PRIVATE src)
if(MSVC)
    target_compile_options(talent-slot-layout-test PRIVATE /UNDEBUG)
endif()
add_executable(motionpath-name-test tests/engine/core/motionpath_name_test.c)
target_include_directories(motionpath-name-test PRIVATE src)
if(MSVC)
    target_compile_options(motionpath-name-test PRIVATE /UNDEBUG)
endif()
include(cmake/Audio.cmake)

option(RAVEN_BUILD_XML2_RUNNER "Build the recovered XML2 diagnostic runner with shared gameplay libraries" OFF)
if(RAVEN_BUILD_XML2_RUNNER)
    if(OPENXML1_BUILD_BOOT_PROBE)
        message(FATAL_ERROR "XML2 scratch layout requires a separate build directory with the XML1 boot probe disabled")
    endif()
    add_compile_definitions(XBOX_AUX_BASE=0x00860000u)
endif()
add_library(xml1-nv2a-vertex STATIC
    src/graphics/dx8/vertex_program.c
    external/nv2a-vsh-cpu/nv2a_vsh_disassembler.c
    external/nv2a-vsh-cpu/nv2a_vsh_cpu.c
    external/nv2a-vsh-cpu/nv2a_vsh_emulator.c
    external/nv2a-vsh-cpu/nv2a_vsh_emulator_execution_state.c)
target_include_directories(xml1-nv2a-vertex PUBLIC external/nv2a-vsh-cpu src)
add_executable(vertex-program-test tests/graphics/dx8/vertex_program_test.c)
target_link_libraries(vertex-program-test PRIVATE xml1-nv2a-vertex)
add_executable(vertex-program-inspect tools/vertex_program_inspect.cpp)
target_link_libraries(vertex-program-inspect PRIVATE xml1-nv2a-vertex)
target_compile_features(vertex-program-inspect PRIVATE cxx_std_17)
add_library(raven-xml1-talent-view STATIC src/engine/progression/raven_xml1_talent_view.c src/engine/progression/character_talent_map_owner.c)
target_include_directories(raven-xml1-talent-view PUBLIC src)
target_compile_definitions(raven-xml1-talent-view PUBLIC XML1_EXPANDED_TALENT_RECORDS=1 XML1_EXPANDED_TALENT_REGISTRY=1)
add_executable(raven-xml1-talent-view-test tests/engine/progression/raven_xml1_talent_view_test.c)
target_link_libraries(raven-xml1-talent-view-test PRIVATE raven-xml1-talent-view)
add_executable(raven-script-strings-test tests/engine/scripting/raven_script_strings_test.c src/engine/scripting/raven_script_strings.c)
target_include_directories(raven-script-strings-test PRIVATE src)
add_library(raven-filter-event STATIC src/engine/combat/raven_filter_event.c)
add_library(raven-bishop-drain STATIC src/engine/combat/raven_bishop_drain.c)
target_include_directories(raven-bishop-drain PUBLIC src)
add_executable(raven-bishop-drain-test tests/engine/combat/raven_bishop_drain_test.c)
target_link_libraries(raven-bishop-drain-test PRIVATE raven-bishop-drain)
target_include_directories(raven-filter-event PUBLIC src)
add_library(raven-filter-event-test SHARED tests/engine/combat/raven_filter_event_exports.c)
target_link_libraries(raven-filter-event-test PRIVATE raven-filter-event)
add_library(raven-talent-curve STATIC src/engine/progression/raven_talent_curve.c)
target_include_directories(raven-talent-curve PUBLIC src)
add_executable(raven-talent-curve-test tests/engine/progression/raven_talent_curve_test.c)
target_link_libraries(raven-talent-curve-test PRIVATE raven-talent-curve)
add_library(raven-talent-data STATIC src/engine/progression/raven_talent_data.cpp src/engine/resources/xmlb.cpp)
target_sources(raven-talent-data PRIVATE src/engine/progression/raven_talent_runtime.cpp)
target_compile_features(raven-talent-data PUBLIC cxx_std_17)
target_include_directories(raven-talent-data PUBLIC src)
target_link_libraries(raven-talent-data PUBLIC raven-talent-curve)
add_executable(raven-talent-data-test tests/engine/progression/raven_talent_data_test.cpp)
target_link_libraries(raven-talent-data-test PRIVATE raven-talent-data)
add_executable(character-talent-record-test tests/engine/progression/character_talent_record_test.c)
target_include_directories(character-talent-record-test PRIVATE src)
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/native-rank-methods.inc"
    COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/guard-talent-ranks.py"
        --generated-dir "${XML1_RECOMP_SOURCE_DIR}"
        --fixture-output "${CMAKE_CURRENT_BINARY_DIR}/native-rank-methods.inc"
    DEPENDS scripts/guard-talent-ranks.py ${XML1_GENERATED_INPUTS}
    VERBATIM)
add_executable(character-talent-native-test tests/engine/progression/character_talent_native_test.c src/engine/progression/character_talent_stream.c
    "${CMAKE_CURRENT_BINARY_DIR}/native-rank-methods.inc")
target_include_directories(character-talent-native-test PRIVATE src "${CMAKE_CURRENT_BINARY_DIR}")
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/native-character-map-methods.inc"
    COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/guard-character-talent-map.py"
        --generated-dir "${XML1_RECOMP_SOURCE_DIR}"
        --fixture-output "${CMAKE_CURRENT_BINARY_DIR}/native-character-map-methods.inc"
    DEPENDS scripts/guard-character-talent-map.py scripts/character_talent_layout.py
        scripts/talent_registry_layout.py ${XML1_GENERATED_INPUTS}
    VERBATIM)
add_executable(character-talent-map-native-test tests/engine/progression/character_talent_map_native_test.c
    src/engine/progression/character_talent_map_owner.c src/engine/progression/character_talent_stream.c
    "${CMAKE_CURRENT_BINARY_DIR}/native-character-map-methods.inc")
target_include_directories(character-talent-map-native-test PRIVATE src "${CMAKE_CURRENT_BINARY_DIR}")
add_executable(character-talent-map-owner-test tests/engine/progression/character_talent_map_owner_test.c
    src/engine/progression/character_talent_map_owner.c)
target_include_directories(character-talent-map-owner-test PRIVATE src)
foreach(talent_save_version IN ITEMS legacy expanded)
    add_executable(character-save-header-${talent_save_version}-test
        tests/engine/progression/character_save_header_test.c src/engine/progression/character_talent_stream.c)
    target_include_directories(character-save-header-${talent_save_version}-test PRIVATE src)
endforeach()
target_compile_definitions(character-save-header-expanded-test PRIVATE XML1_EXPANDED_TALENT_RECORDS=1)
add_library(raven-actor-talent STATIC src/engine/progression/raven_actor_talent.cpp src/engine/progression/raven_talent_binding.cpp src/engine/progression/raven_talent_context.cpp src/engine/powers/raven_event_operands.cpp src/engine/core/raven_numeric.c src/engine/combat/raven_damage_records.cpp)
target_link_libraries(raven-actor-talent PUBLIC raven-talent-data raven-xml1-talent-view)
target_sources(raven-actor-talent PRIVATE src/engine/powers/raven_xml2_powerup_view.c)
target_sources(raven-actor-talent PRIVATE src/engine/powers/raven_xml1_powerup_view.c)
target_sources(raven-actor-talent PRIVATE src/engine/powers/raven_powerup_metadata.cpp)
target_sources(raven-actor-talent PRIVATE src/engine/powers/raven_shared_powerups.cpp)
add_executable(raven-shared-powerups-test tests/engine/powers/raven_shared_powerups_test.cpp)
target_link_libraries(raven-shared-powerups-test PRIVATE raven-actor-talent)
target_sources(raven-actor-talent PRIVATE src/engine/combat/raven_harming_operand.cpp)
target_sources(raven-actor-talent PRIVATE src/engine/combat/raven_add_attack.cpp src/engine/combat/raven_rating.cpp src/engine/combat/raven_percent_attack.cpp src/engine/combat/raven_absorb.cpp src/engine/combat/raven_vacuum.c src/engine/scripting/raven_remove_events.c)
target_sources(raven-actor-talent PRIVATE src/engine/combat/raven_harming_settings.c)
target_sources(raven-actor-talent PRIVATE src/engine/progression/raven_imported_talents.cpp)
target_sources(raven-actor-talent PRIVATE src/engine/powers/raven_power_bindings.cpp)
add_executable(raven-power-bindings-test tests/engine/powers/raven_power_bindings_test.cpp)
target_link_libraries(raven-power-bindings-test PRIVATE raven-actor-talent)
target_sources(raven-actor-talent PRIVATE src/engine/powers/raven_power_rotation.cpp)
add_executable(raven-power-rotation-test tests/engine/powers/raven_power_rotation_test.cpp)
target_link_libraries(raven-power-rotation-test PRIVATE raven-actor-talent)
add_executable(character-save-header-selections-test tests/engine/progression/character_save_header_test.c
    tests/power_selection_save_fixture.cpp src/engine/progression/character_talent_stream.c)
target_link_libraries(character-save-header-selections-test PRIVATE raven-actor-talent)
target_compile_definitions(character-save-header-selections-test PRIVATE XML1_PERSISTENT_POWER_SELECTIONS=1)
add_executable(raven-imported-talents-test tests/engine/progression/raven_imported_talents_test.cpp)
target_link_libraries(raven-imported-talents-test PRIVATE raven-actor-talent)
add_executable(raven-harming-operand-test tests/engine/combat/raven_harming_operand_test.cpp)
target_link_libraries(raven-harming-operand-test PRIVATE raven-actor-talent)
add_executable(raven-powerup-metadata-test tests/engine/powers/raven_powerup_metadata_test.cpp)
target_link_libraries(raven-powerup-metadata-test PRIVATE raven-actor-talent)
target_sources(raven-actor-talent PRIVATE src/diagnostics/raven_xml2_query_probe.cpp)
set(RAVEN_XML2_RECOVERY_SOURCE "" CACHE PATH "Local recovered XML2 source for query instrumentation checks")
if(RAVEN_XML2_RECOVERY_SOURCE)
    set(_xml2_gen "${RAVEN_XML2_RECOVERY_SOURCE}/src/recomp/gen")
    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${_xml2_gen}/recomp_0029.c")
    file(READ "${_xml2_gen}/recomp_0029.c" _xml2_chunk)
    string(REGEX MATCHALL "void sub_0015E8C0\\(void\\)" _xml2_definitions "${_xml2_chunk}")
    list(LENGTH _xml2_definitions _xml2_definition_count)
    if(NOT _xml2_definition_count EQUAL 1)
        message(FATAL_ERROR "Expected exactly one XML2 15E8C0 definition for instrumentation")
    endif()
    string(REPLACE "void sub_0015E8C0(void)" "void xml2_native_query_body(void)" _xml2_chunk "${_xml2_chunk}")
    file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/xml2-query-instrumented.c" CONTENT "${_xml2_chunk}")
    add_library(raven-xml2-query-instrumented OBJECT
        "${CMAKE_CURRENT_BINARY_DIR}/xml2-query-instrumented.c" src/diagnostics/raven_xml2_query_guest.c)
    target_include_directories(raven-xml2-query-instrumented PRIVATE "${_xml2_gen}" src)
    add_executable(raven-xml2-query-guest-test tests/diagnostics/raven_xml2_query_guest_test.c src/diagnostics/raven_xml2_query_guest.c)
    target_include_directories(raven-xml2-query-guest-test PRIVATE "${_xml2_gen}" src)
    target_link_libraries(raven-xml2-query-guest-test PRIVATE raven-actor-talent)
    if(MSVC)
        target_compile_options(raven-xml2-query-instrumented PRIVATE /bigobj)
    endif()
endif()
add_executable(raven-powerup-view-test tests/engine/powers/raven_powerup_view_test.c)
target_link_libraries(raven-powerup-view-test PRIVATE raven-actor-talent)
add_executable(raven-numeric-test tests/engine/core/raven_numeric_test.c)
target_link_libraries(raven-numeric-test PRIVATE raven-actor-talent)
add_executable(raven-damage-records-test tests/engine/combat/raven_damage_records_test.cpp)
add_executable(raven-native-damage-test tests/engine/combat/raven_native_damage_test.cpp src/engine/combat/raven_native_damage.cpp)
target_compile_definitions(raven-native-damage-test PRIVATE XML1_RAVEN_OPERAND_TRACE=1)
target_link_libraries(raven-native-damage-test PRIVATE raven-actor-talent)
target_link_libraries(raven-damage-records-test PRIVATE raven-actor-talent)
add_executable(raven-actor-talent-test tests/engine/progression/raven_actor_talent_test.cpp)
target_link_libraries(raven-actor-talent-test PRIVATE raven-actor-talent)
add_executable(raven-talent-context-test tests/engine/progression/raven_talent_context_test.cpp)
target_link_libraries(raven-talent-context-test PRIVATE raven-actor-talent)
add_executable(raven-affecter-query-test tests/raven_affecter_query_test.cpp)
target_link_libraries(raven-affecter-query-test PRIVATE raven-actor-talent)
# Preserve the recovered evaluator's multiply/add rounding order.
if(MSVC)
    target_compile_options(raven-talent-curve PRIVATE /fp:strict)
    target_compile_options(raven-talent-curve-test PRIVATE /fp:strict)
endif()
option(RAVEN_XML2_CURVE_REFERENCE "Compare against locally extracted XML2 blend arithmetic" OFF)
if(RAVEN_XML2_CURVE_REFERENCE)
    if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/build/xml2-talent-curve-reference.inc")
        message(FATAL_ERROR "Generate build/xml2-talent-curve-reference.inc from the local XML2 recovery with scripts/generate-xml2-talent-curve-reference.py first")
    endif()
    target_compile_definitions(raven-talent-curve-test PRIVATE RAVEN_XML2_CURVE_REFERENCE=1)
    target_include_directories(raven-talent-curve-test PRIVATE build)
endif()
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/x87-status-fixture.inc"
    COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate-x87-status-test.py"
        --output "${CMAKE_CURRENT_BINARY_DIR}/x87-status-fixture.inc"
    DEPENDS scripts/generate-x87-status-test.py external/xboxrecomp/tools/recomp/lifter.py)
add_executable(x87-status-test tests/x87_status_test.c "${CMAKE_CURRENT_BINARY_DIR}/x87-status-fixture.inc")
target_include_directories(x87-status-test PRIVATE external/xboxrecomp/templates/runtime "${CMAKE_CURRENT_BINARY_DIR}")

if(NOT WIN32 OR NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
    message(FATAL_ERROR "The initial XboxRecomp baseline requires Windows x64.")
endif()

# The generated game uses this runtime for CPU/kernel diagnostics.
# Upstream xbox_d3d8 currently uses D3D11: this is NOT the final DX8 backend.
add_subdirectory(external/xboxrecomp)
set(LIBSAMPLERATE_EXAMPLES OFF CACHE BOOL "" FORCE)
set(LIBSAMPLERATE_INSTALL OFF CACHE BOOL "" FORCE)
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
set(CMAKE_POLICY_VERSION_MINIMUM 3.5)
add_subdirectory(external/libsamplerate)
unset(CMAKE_POLICY_VERSION_MINIMUM)
target_link_libraries(xbox_apu PUBLIC SampleRate::samplerate)
target_compile_definitions(xbox_apu PUBLIC RECOMP_APU_LIBSAMPLERATE=1)
target_sources(xbox_kernel PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/runtime/guest/guest_physical.c")
target_compile_definitions(xbox_kernel PRIVATE RECOMP_GUEST_PHYSICAL_PAGES=1)
add_subdirectory(external/xemu-dsp)
get_target_property(XML1_APU_SOURCES xbox_apu SOURCES)
list(FILTER XML1_APU_SOURCES EXCLUDE REGEX "apu_dsp[.]c$")
set_property(TARGET xbox_apu PROPERTY SOURCES ${XML1_APU_SOURCES})
target_sources(xbox_apu PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/audio/apu_dsp_real.c")
target_sources(xbox_apu PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/audio/apu_output.c")
target_compile_definitions(xbox_apu PRIVATE RECOMP_APU_NATIVE_DSP_OUTPUT=1)
target_include_directories(xbox_apu PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_sources(xbox_apu PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/audio/ac97_registers.c")
target_link_libraries(xbox_apu PUBLIC xml1-dsp)
target_compile_options(xbox_apu PRIVATE /UNDEBUG)
add_executable(apu-dsp-test tests/audio/apu_dsp_test.c)
add_executable(apu-output-lock-test tests/audio/apu_output_lock_test.c)
target_link_libraries(apu-output-lock-test PRIVATE xboxrecomp)
target_include_directories(apu-output-lock-test PRIVATE external/xboxrecomp/src)
add_executable(apu-resample-test tests/audio/apu_resample_test.c)
target_link_libraries(apu-resample-test PRIVATE xboxrecomp)
target_include_directories(apu-resample-test PRIVATE external/xboxrecomp/src)
add_executable(fair-gate-test tests/platform/windows/fair_gate_test.c)
add_executable(controller-retry-test tests/controller_retry_test.c)
add_executable(shared-completion-test tests/platform/windows/shared_completion_test.c)
target_include_directories(shared-completion-test PRIVATE src)
add_executable(light-state-test tests/graphics/dx8/light_state_test.c)
target_include_directories(light-state-test PRIVATE src)
target_include_directories(fair-gate-test PRIVATE src)
add_executable(guest-fist-test tests/guest_fist_test.c)
target_include_directories(guest-fist-test PRIVATE external/xboxrecomp/templates/runtime external/xboxrecomp/src)
add_executable(guest-physical-test tests/runtime/guest/guest_physical_test.c src/runtime/guest/guest_physical.c)
target_include_directories(guest-physical-test PRIVATE external/xboxrecomp/src/kernel external/xboxrecomp/src)
add_executable(apu-output-test tests/audio/apu_output_test.c src/audio/apu_output.c)
target_include_directories(apu-output-test PRIVATE src external/xboxrecomp/src/apu)
target_link_libraries(apu-dsp-test PRIVATE xboxrecomp)
target_include_directories(apu-dsp-test PRIVATE external/xboxrecomp/src)
add_executable(ac97-register-test tests/audio/ac97_register_test.c src/audio/ac97_registers.c)
add_executable(kernel-dispatch-thread-test tests/kernel_dispatch_thread_test.c)
add_executable(idex-channel-test tests/idex_channel_test.c)
add_executable(device-irq-test tests/device_irq_test.c)
add_executable(irql-exclusion-test tests/irql_exclusion_test.c)
target_include_directories(irql-exclusion-test PRIVATE external/xboxrecomp/src/kernel external/xboxrecomp/src)
target_link_libraries(irql-exclusion-test PRIVATE xboxrecomp)
target_include_directories(device-irq-test PRIVATE external/xboxrecomp/src/kernel)
target_link_libraries(device-irq-test PRIVATE xboxrecomp)
target_include_directories(idex-channel-test PRIVATE external/xboxrecomp/src/kernel)
target_link_libraries(idex-channel-test PRIVATE xboxrecomp)
target_include_directories(kernel-dispatch-thread-test PRIVATE external/xboxrecomp/src/kernel)
target_link_libraries(kernel-dispatch-thread-test PRIVATE xboxrecomp)
add_executable(directory-lifetime-test tests/directory_lifetime_test.c)
target_link_libraries(directory-lifetime-test PRIVATE xboxrecomp)
add_executable(guest-vmem-test tests/guest_vmem_test.c external/xboxrecomp/src/kernel/guest_vmem.c)
target_include_directories(guest-vmem-test PRIVATE external/xboxrecomp/src/kernel external/xboxrecomp/src)
add_executable(guest-crt-test tests/runtime/guest/guest_crt_test.c src/runtime/guest/guest_crt.c)
target_include_directories(guest-crt-test PRIVATE external/xboxrecomp/src/kernel external/xboxrecomp/src)
add_executable(guest-input-test tests/input/guest_input_test.c src/input/guest_input.c)
target_include_directories(guest-input-test PRIVATE src external/xboxrecomp/src/input external/xboxrecomp/src/kernel external/xboxrecomp/src)
message(STATUS "OpenXML1: native DX8 game with committed guest source")

option(OPENXML1_BUILD_BOOT_PROBE "Build the XML1 game executable with embedded DX8 renderer" OFF)
option(OPENXML1_OPTIMIZE_RECOMP "Optimize generated game code for playback testing" OFF)
if(OPENXML1_BUILD_BOOT_PROBE)
    file(GLOB_RECURSE RECOMP_SOURCES CONFIGURE_DEPENDS "${XML1_RECOMP_SOURCE_DIR}/*.c")
    if(NOT RECOMP_SOURCES)
        message(FATAL_ERROR "Committed guest sources are missing. Restore src/recomp/game or select a complete XML1_RECOMP_SOURCE_DIR.")
    endif()
    add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/flags-fixture.inc"
        COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate-flags-test.py"
        --output "${CMAKE_CURRENT_BINARY_DIR}/flags-fixture.inc"
        DEPENDS scripts/generate-flags-test.py external/xboxrecomp/tools/recomp/translator.py
                external/xboxrecomp/tools/recomp/test_flag_join.py external/xboxrecomp/tools/recomp/lifter.py
                external/xboxrecomp/tools/recomp/test_incdec_carry.py)
    add_executable(guest-flags-test tests/guest_flags_test.c "${CMAKE_CURRENT_BINARY_DIR}/flags-fixture.inc")
    target_include_directories(guest-flags-test PRIVATE src "${XML1_RECOMP_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}")
    add_executable(xml1-boot-probe src/engine/resources/asset_routes.c src/app/build_settings.c src/app/main.c src/diagnostics/performance_log.c src/game/xml1/newgame_plus.c src/diagnostics/native_probe.c src/runtime/guest/recomp_manual.c src/runtime/guest/guest_crt.c src/input/guest_input.c src/graphics/dx8/guest_graphics_trace.c src/graphics/dx8/guest_graphics_live.c src/audio/dsound_hle.c src/audio/dsound_mixer.c src/audio/dsound_output.c tests/guest_swizzle_test.c tests/engine/progression/progression_test.c ${RECOMP_SOURCES})
    target_include_directories(xml1-boot-probe PRIVATE src "${XML1_RECOMP_SOURCE_DIR}" external/xboxrecomp/include external/xboxrecomp/src/apu)
    target_sources(xml1-boot-probe PRIVATE src/diagnostics/raven_operand_trace.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/progression/character_talent_stream.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/progression/character_talent_map_guest.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/combat/raven_vitality_bonus.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/actors/raven_leader_power.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/actors/raven_leader_event_guest.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/actors/raven_leader_stream.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/actors/raven_leader_definition.cpp src/engine/actors/raven_leader_definition_guest.c src/engine/actors/raven_transform_definition_guest.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/actors/raven_leader_actor.c)
    target_compile_definitions(xml1-boot-probe PRIVATE XML1_PERMANENT_VITALITY=1)
    target_sources(xml1-boot-probe PRIVATE src/engine/progression/talent_source_table.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/progression/talent_package_guest.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/progression/talent_registry_guest.c src/engine/progression/talent_identity_guest.c src/engine/progression/talent_identity_save.c)
    target_compile_definitions(xml1-boot-probe PRIVATE XML1_STABLE_TALENT_IDENTITIES=1)
    target_compile_definitions(xml1-boot-probe PRIVATE XML1_EXPANDED_TALENT_RECORDS=1)
    target_compile_definitions(xml1-boot-probe PRIVATE XML1_PERSISTENT_POWER_SELECTIONS=1)
    target_sources(xml1-boot-probe PRIVATE tests/contiguous_reuse_test.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/progression/raven_actor_talent_probe.cpp)
    target_sources(xml1-boot-probe PRIVATE src/engine/combat/raven_native_energy.cpp)
    target_sources(xml1-boot-probe PRIVATE src/engine/combat/raven_native_energy_guest.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/powers/raven_power_bindings_guest.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/combat/raven_native_damage.cpp)
    target_sources(xml1-boot-probe PRIVATE tests/engine/powers/raven_powerup_guest_test.c tests/engine/powers/raven_powerup_guest_fixture.cpp tests/engine/combat/raven_filter_guest_test.c)
    target_link_libraries(xml1-boot-probe PRIVATE xboxrecomp dbghelp raven-actor-talent raven-filter-event raven-pc-sound-cache)
    target_sources(xml1-boot-probe PRIVATE src/engine/combat/raven_bishop_guest.c)
    target_link_libraries(xml1-boot-probe PRIVATE raven-bishop-drain)
    target_link_libraries(xml1-boot-probe PRIVATE raven-xml1-talent-view)
    target_compile_definitions(xml1-boot-probe PRIVATE XML1_RAVEN_OPERAND_TRACE=1)
    target_include_directories(xml1-boot-probe PRIVATE src "${XML1_RECOMP_SOURCE_DIR}" external/xboxrecomp/include external/xboxrecomp/src/apu)
    target_link_libraries(xml1-boot-probe PRIVATE xboxrecomp dbghelp xml1-nv2a-vertex)
    target_sources(xml1-boot-probe PRIVATE src/engine/scripting/raven_script_strings.c src/engine/scripting/raven_script_extensions.c src/engine/combat/raven_filter_guest.c src/audio/raven_effect_sound_guest.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/actors/raven_character_race.cpp src/engine/actors/raven_character_race_guest.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/world/raven_extraction_registry.cpp src/engine/world/raven_extraction_owner.cpp src/engine/world/raven_extraction_guest.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/world/raven_progress_guest.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/world/raven_campaign_state_guest.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/world/raven_campaign_menu_guest.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/saves/raven_saved_stats_guest.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/powers/raven_powerup_guest.c)
    target_sources(xml1-boot-probe PRIVATE src/engine/powers/raven_victim_event_guest.c)
    target_sources(xml1-boot-probe PRIVATE src/audio/raven_loop_sound_runtime.cpp src/audio/raven_loop_sound_guest.c)
    target_link_libraries(xml1-boot-probe PRIVATE raven-loop-sound)
    target_sources(xml1-boot-probe PRIVATE src/engine/combat/raven_harming_callbacks.c)
    target_sources(xml1-boot-probe PRIVATE tests/raven_physical_health_test.c)
    target_compile_definitions(xml1-boot-probe PRIVATE XML1_SCRIPT_EXTENSIONS=1)
    target_compile_definitions(xml1-boot-probe PRIVATE _CRT_SECURE_NO_WARNINGS WIN32_LEAN_AND_MEAN NOMINMAX)
    # Fast diagnostic iterations first; benchmark only a separately optimized build.
    target_compile_options(xml1-boot-probe PRIVATE /bigobj /Zi /MP4 /FIdiagnostics/diagnostics.h)
    if(OPENXML1_OPTIMIZE_RECOMP)
        target_compile_options(xml1-boot-probe PRIVATE /O2 /Ob1)
    else()
        target_compile_options(xml1-boot-probe PRIVATE /Od /Ob0)
    endif()
    target_link_options(xml1-boot-probe PRIVATE /DEBUG /INCREMENTAL:NO /STACK:16777216)
endif()
add_executable(kernel-directory-test tests/kernel_directory_test.c)
target_include_directories(kernel-directory-test PRIVATE external/xboxrecomp/src/kernel)
target_link_libraries(kernel-directory-test PRIVATE xboxrecomp)

add_executable(worker-lifetime-test tests/platform/windows/worker_lifetime_test.c)
add_executable(texture-wire-cache-test tests/graphics/dx8/texture_wire_cache_test.c)
target_include_directories(texture-wire-cache-test PRIVATE src)
add_executable(video-settings-test tests/video_settings_test.c)
target_include_directories(video-settings-test PRIVATE external/xboxrecomp/src/kernel)
target_link_libraries(video-settings-test PRIVATE xboxrecomp)
target_include_directories(worker-lifetime-test PRIVATE src)
if(OPENXML1_BUILD_BOOT_PROBE)
    add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/fence-wait-fixture.inc"
        COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate-fence-wait-test.py"
        --output "${CMAKE_CURRENT_BINARY_DIR}/fence-wait-fixture.inc"
        --generated-dir "${XML1_RECOMP_SOURCE_DIR}"
        DEPENDS scripts/generate-fence-wait-test.py ${XML1_GENERATED_INPUTS})
    add_executable(graphics-fence-wait-test tests/graphics_fence_wait_test.c "${CMAKE_CURRENT_BINARY_DIR}/fence-wait-fixture.inc")
    target_include_directories(graphics-fence-wait-test PRIVATE external/nv2a-vsh-cpu src "${XML1_RECOMP_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}" external/xboxrecomp/src/kernel external/xboxrecomp/src/input external/xboxrecomp/src)
    target_link_libraries(graphics-fence-wait-test PRIVATE xml1-nv2a-vertex)
endif()

add_executable(build-settings-test tests/app/build_settings_test.c src/app/build_settings.c)
add_executable(save-name-test tests/engine/saves/save_name_test.c)
target_include_directories(save-name-test PRIVATE src)
target_include_directories(build-settings-test PRIVATE src)

add_library(xml1-miniz STATIC external/miniz/miniz.c external/miniz/miniz_tinfl.c external/miniz/miniz_tdef.c external/miniz/miniz_zip.c)
target_include_directories(xml1-miniz PUBLIC external/miniz)
target_compile_definitions(xml1-miniz PUBLIC MINIZ_NO_ARCHIVE_WRITING_APIS)
add_library(xml1-loose-extract STATIC src/app/loose_extract.cpp src/engine/resources/xmlb.cpp)
target_compile_features(xml1-loose-extract PUBLIC cxx_std_17)
target_compile_definitions(xml1-loose-extract PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN _CRT_SECURE_NO_WARNINGS)
target_include_directories(xml1-loose-extract PUBLIC src)
target_link_libraries(xml1-loose-extract PRIVATE xml1-miniz bcrypt)
add_executable(loose-extract-driver tests/app/loose_extract_driver.cpp)
target_link_libraries(loose-extract-driver PRIVATE xml1-loose-extract)

target_sources(xml1-loose-extract PRIVATE src/app/loose_setup.cpp)
target_link_libraries(xml1-loose-extract PRIVATE comctl32)
if(TARGET xml1-boot-probe)
    target_link_libraries(xml1-boot-probe PRIVATE xml1-loose-extract)
endif()
add_executable(asset-routes-test tests/engine/resources/asset_routes_test.c src/engine/resources/asset_routes.c)
target_include_directories(asset-routes-test PRIVATE src)
add_executable(kernel-asset-filter-test tests/kernel_asset_filter_test.c src/engine/resources/asset_routes.c)
target_include_directories(kernel-asset-filter-test PRIVATE src external/xboxrecomp/src/kernel)
target_link_libraries(kernel-asset-filter-test PRIVATE xboxrecomp)

target_sources(xml1-loose-extract PRIVATE src/engine/resources/pkgb_decode.cpp)
target_sources(xml1-loose-extract PRIVATE src/ui/pc_menu.cpp)
add_library(xml1-pc-controls STATIC src/input/pc_controls.cpp)
target_sources(xml1-pc-controls PRIVATE src/input/pc_input_channel.cpp src/ui/pc_options_model.cpp)
target_sources(xml1-pc-controls PRIVATE src/input/pc_gamepad.cpp)
target_sources(xml1-pc-controls PRIVATE src/ui/pc_prompts.cpp)
target_sources(xml1-pc-controls PRIVATE src/ui/pc_native_options.cpp)
target_compile_features(xml1-pc-controls PUBLIC cxx_std_17)
target_compile_definitions(xml1-pc-controls PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN)
target_include_directories(xml1-pc-controls PUBLIC src)
target_link_libraries(guest-input-test PRIVATE xml1-pc-controls)
add_executable(pc-input-integration-test tests/input/pc_input_integration_test.c src/input/guest_input.c)
target_include_directories(pc-input-integration-test PRIVATE src external/xboxrecomp/src/input external/xboxrecomp/src/kernel external/xboxrecomp/src)
target_link_libraries(pc-input-integration-test PRIVATE xml1-pc-controls)
if(TARGET xml1-boot-probe)
    target_link_libraries(xml1-boot-probe PRIVATE xml1-pc-controls)
    target_sources(xml1-boot-probe PRIVATE src/ui/pc_prompt_guest.c)
endif()
add_executable(pc-controls-test tests/input/pc_controls_test.cpp)
target_link_libraries(pc-controls-test PRIVATE xml1-pc-controls)
add_executable(pkgb-decode-driver tests/engine/resources/pkgb_decode_driver.cpp)
target_link_libraries(pkgb-decode-driver PRIVATE xml1-loose-extract)

if(TARGET xml1-boot-probe)
    # DX8 is 32-bit; carry its static-CRT executable as an embedded resource.
    set(XML1_RENDERER_BUILD "${CMAKE_CURRENT_BINARY_DIR}/embedded-renderer")
    add_custom_target(xml1-embedded-renderer
        COMMAND "${CMAKE_COMMAND}" -S "${CMAKE_CURRENT_SOURCE_DIR}/renderer" -B "${XML1_RENDERER_BUILD}" -A Win32
        COMMAND "${CMAKE_COMMAND}" --build "${XML1_RENDERER_BUILD}" --config $<CONFIG> --target xml1-dx8-worker
        BYPRODUCTS "${XML1_RENDERER_BUILD}/$<CONFIG>/xml1-dx8-worker.exe")
    file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/embedded-worker-$<CONFIG>.rc"
        CONTENT "101 RCDATA \"${XML1_RENDERER_BUILD}/$<CONFIG>/xml1-dx8-worker.exe\"\n")
    target_sources(xml1-boot-probe PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/embedded-worker-$<CONFIG>.rc" src/platform/windows/embedded_worker.c)
    add_dependencies(xml1-boot-probe xml1-embedded-renderer)
    target_link_libraries(xml1-boot-probe PRIVATE bcrypt)
endif()

add_executable(xmlb-driver tests/engine/resources/xmlb_driver.cpp)
target_link_libraries(xmlb-driver PRIVATE xml1-loose-extract)

add_executable(xmlb-test tests/engine/resources/xmlb_test.cpp)
target_link_libraries(xmlb-test PRIVATE xml1-loose-extract)
add_executable(raven-extraction-registry-test tests/engine/world/raven_extraction_registry_test.cpp src/engine/world/raven_extraction_registry.cpp src/engine/world/raven_extraction_owner.cpp)
target_link_libraries(raven-extraction-registry-test PRIVATE xml1-loose-extract)
add_executable(raven-saved-stats-stream-test tests/engine/saves/raven_saved_stats_stream_test.c)
target_include_directories(raven-saved-stats-stream-test PRIVATE src)
add_executable(raven-progress-extension-test tests/engine/world/raven_progress_extension_test.c)
target_include_directories(raven-progress-extension-test PRIVATE src)
if(RAVEN_BUILD_XML2_RUNNER)
    include(cmake/XML2Runner.cmake)
endif()

# Persistent identities must not depend on runtime talent registration order.
add_executable(talent-identity-save-test tests/engine/progression/talent_identity_save_test.c src/engine/progression/talent_identity_save.c)
add_executable(raven-vitality-bonus-test tests/engine/combat/raven_vitality_bonus_test.c src/engine/combat/raven_vitality_bonus.c)
add_executable(raven-auto-equip-inventory-test tests/engine/inventory/raven_auto_equip_inventory_test.c)
target_include_directories(raven-auto-equip-inventory-test PRIVATE src)
add_executable(raven-leader-power-test tests/engine/actors/raven_leader_power_test.c src/engine/actors/raven_leader_power.c)
add_executable(raven-leader-stream-test tests/engine/actors/raven_leader_stream_test.c src/engine/actors/raven_leader_stream.c src/engine/actors/raven_leader_power.c)
target_include_directories(raven-leader-stream-test PRIVATE src)
target_include_directories(raven-leader-power-test PRIVATE src)
target_include_directories(raven-vitality-bonus-test PRIVATE src)

add_executable(raven-actor-scale-test tests/engine/actors/raven_actor_scale_test.c src/engine/actors/raven_actor_scale.c)
target_include_directories(raven-actor-scale-test PRIVATE src)

add_library(raven-actor-scale STATIC src/engine/actors/raven_actor_scale.c src/engine/actors/raven_actor_scale_owner.cpp)
add_library(raven-stash STATIC src/engine/inventory/raven_stash_owner.cpp)
target_include_directories(raven-stash PUBLIC src)
target_compile_features(raven-stash PUBLIC cxx_std_17)
add_executable(raven-stash-owner-test tests/engine/inventory/raven_stash_owner_test.cpp)
target_link_libraries(raven-stash-owner-test PRIVATE raven-stash)
if(TARGET xml1-boot-probe)
    target_link_libraries(xml1-boot-probe PRIVATE raven-stash)
    target_sources(xml1-boot-probe PRIVATE src/engine/inventory/raven_stash_stream_guest.c)
endif()
target_sources(raven-actor-scale PRIVATE src/engine/actors/raven_actor_transform_owner.cpp)
add_executable(raven-actor-transform-owner-test tests/engine/actors/raven_actor_transform_owner_test.cpp)
target_link_libraries(raven-actor-transform-owner-test PRIVATE raven-actor-scale)
target_sources(raven-actor-scale PRIVATE src/engine/actors/raven_actor_scale_clearance.c)
target_include_directories(raven-actor-scale PUBLIC src)
target_compile_features(raven-actor-scale PUBLIC cxx_std_17)
add_executable(raven-actor-scale-owner-test tests/engine/actors/raven_actor_scale_owner_test.cpp)
target_link_libraries(raven-actor-scale-owner-test PRIVATE raven-actor-scale)
add_executable(raven-actor-scale-clearance-test tests/engine/actors/raven_actor_scale_clearance_test.cpp)
target_link_libraries(raven-actor-scale-clearance-test PRIVATE raven-actor-scale)
if(TARGET xml1-boot-probe)
    target_link_libraries(xml1-boot-probe PRIVATE raven-actor-scale)
endif()
target_include_directories(talent-identity-save-test PRIVATE src)

add_executable(character-save-header-identities-test tests/engine/progression/character_save_header_test.c
    tests/power_selection_save_fixture.cpp tests/engine/progression/talent_identity_header_fixture.c
    src/engine/progression/character_talent_stream.c src/engine/progression/talent_identity_save.c)
target_link_libraries(character-save-header-identities-test PRIVATE raven-actor-talent)
target_compile_definitions(character-save-header-identities-test PRIVATE
    XML1_PERSISTENT_POWER_SELECTIONS=1 XML1_STABLE_TALENT_IDENTITIES=1)

add_executable(character-save-header-vitality-test tests/engine/progression/character_save_header_test.c
    tests/power_selection_save_fixture.cpp tests/engine/progression/talent_identity_header_fixture.c
    src/engine/progression/character_talent_stream.c src/engine/progression/talent_identity_save.c src/engine/combat/raven_vitality_bonus.c)
target_link_libraries(character-save-header-vitality-test PRIVATE raven-actor-talent)
target_compile_definitions(character-save-header-vitality-test PRIVATE
    XML1_PERSISTENT_POWER_SELECTIONS=1 XML1_STABLE_TALENT_IDENTITIES=1 XML1_PERMANENT_VITALITY=1)

target_sources(raven-actor-talent PRIVATE src/engine/actors/raven_ai_modes.cpp)
add_executable(raven-ai-modes-test tests/engine/actors/raven_ai_modes_test.cpp)
target_link_libraries(raven-ai-modes-test PRIVATE raven-actor-talent)
if(TARGET xml1-boot-probe)
    target_sources(xml1-boot-probe PRIVATE src/engine/actors/raven_ai_modes_guest.c)
endif()
