# gh - ReXGlue Recompiled Project
#
# SDK boilerplate lives in generated/rexglue.cmake.

cmake_minimum_required(VERSION 3.25)
project(gh LANGUAGES C CXX)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# The SDK's byte-swap helpers use SSSE3 / SSE4.1 intrinsics (stuntdouble does the same).
if(NOT MSVC AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
    add_compile_options(-mssse3 -msse4.1)
endif()

if(NINTENDO_SWITCH)
    # Handed to the SDK (switch/sdk-edits.py): the Horizon replacements for its POSIX sources.
    set(GH_SWITCH_HOST "${CMAKE_CURRENT_SOURCE_DIR}/switch/host")
    set(REXGLUE_SWITCH_INCLUDE_DIR "${GH_SWITCH_HOST}/include")
    set(REXGLUE_SWITCH_CORE_SOURCES
        ${GH_SWITCH_HOST}/memory_nx.cpp
        ${GH_SWITCH_HOST}/mapped_memory_nx.cpp
        ${GH_SWITCH_HOST}/fiber_nx.cpp
        ${GH_SWITCH_HOST}/exception_handler_nx.cpp
        ${GH_SWITCH_HOST}/seh_nx.cpp)
    set(REXGLUE_SWITCH_UI_SOURCES
        ${GH_SWITCH_HOST}/ui/window_nx.cpp
        ${GH_SWITCH_HOST}/ui/windowed_app_context_nx.cpp)
    set(REXGLUE_SWITCH_MAIN_SOURCE ${GH_SWITCH_HOST}/ui/windowed_app_main_nx.cpp)
    set(REXGLUE_SWITCH_INPUT_SOURCES ${GH_SWITCH_HOST}/input/nx_input_driver.cpp)
    # newlib hides POSIX declarations (fileno, fdopen, tm_gmtoff) under strict -std=c++23.
    add_compile_definitions(_GNU_SOURCE)
    # Horizon has no time zones (newlib has no tm_gmtoff): spdlog logs in UTC.
    add_compile_definitions(SPDLOG_NO_TZ_OFFSET)
    # vulkan.hpp has no dlopen for this platform; the driver is linked statically.
    add_compile_definitions(VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL=0)
    # newlib/libnx gaps, in every translation unit.
    add_compile_options("$<$<COMPILE_LANGUAGE:C,CXX>:-include>"
                        "$<$<COMPILE_LANGUAGE:C,CXX>:${REXGLUE_SWITCH_INCLUDE_DIR}/switch_compat.h>")
    include_directories(${REXGLUE_SWITCH_INCLUDE_DIR})
    # generated/rexglue.cmake (rewritten by every codegen run) runs the rexglue CLI as a build step:
    # the cross build takes the host-built one.
    set(REXGLUE_HOST_CLI "${REXSDK_DIR}/out/linux-amd64/Release/rexglue" CACHE FILEPATH "Host rexglue CLI")
    add_executable(rex::rexglue IMPORTED GLOBAL)
    set_target_properties(rex::rexglue PROPERTIES IMPORTED_LOCATION "${REXGLUE_HOST_CLI}")
endif()

# GPU backends from this tree, compiled into the SDK's GPU plugin (switch/sdk-edits.py adds the hook):
# the trace backend (--gpu_backend=trace: the command processor runs, every draw's state is logged,
# nothing is drawn) on every platform; the deko3d renderer on the Switch (docs/native-renderer-plan.md).
set(REXGPU_XENOS_EXTRA_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/gpu/trace_backend.cpp")
if(NINTENDO_SWITCH)
    list(APPEND REXGPU_XENOS_EXTRA_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/gpu/deko3d_backend.cpp")
    # gh: sprite replacement store used by the deko3d backend (no GPU code, host-testable: tools/sprite_mod_test.py)
    list(APPEND REXGPU_XENOS_EXTRA_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/sprite_mod.cpp")
endif()

include(generated/rexglue.cmake)

# Sources
set(GH_SOURCES
    src/main.cpp
    src/kernel_stubs.cpp
)
if(NOT NINTENDO_SWITCH)
    # The ImGui settings window (Back/Select or F1); the Switch has no ImGui overlay under deko3d.
    list(APPEND GH_SOURCES src/gh_settings.cpp)
endif()

if(WIN32)
    add_executable(gh WIN32 ${GH_SOURCES})
else()
    add_executable(gh ${GH_SOURCES})
endif()

rexglue_setup_target(gh GPU_PLUGINS xenos)
# Census of the game's Direct3D entry points (docs/native-renderer-plan.md M1): counting wrappers
# for every recompiled function in the D3D range, generated by tools/census_gen.py. Linux only.
option(GH_CENSUS "Count calls of the recompiled functions in the Direct3D range" OFF)
if(GH_CENSUS)
    target_sources(gh PRIVATE src/census/census_generated.cpp)
endif()

# The SDK's UI headers include imgui.h (headers only: ImGui is compiled into rexruntime).
if(REXSDK_DIR)
    target_include_directories(gh PRIVATE "${REXSDK_DIR}/thirdparty/imgui")
endif()

# --- Nintendo Switch (devkitA64 + libnx + mesa-switch NVK), see CLAUDE.md step 4 ---------------
if(NINTENDO_SWITCH)
    set(NVK_SDK "" CACHE PATH "mesa-switch NVK install (lib/libvulkan.a)")
    set(GH_SWITCH_HOST "${CMAKE_CURRENT_SOURCE_DIR}/switch/host")
    target_sources(gh PRIVATE
        ${GH_SWITCH_HOST}/switch_nx.cpp
        ${GH_SWITCH_HOST}/membench_nx.cpp
        ${GH_SWITCH_HOST}/switch_compat.c
    )
    set(GH_VERSION "0.0.unknown" CACHE STRING "NACP display version, from git (switch/build.sh)")
    # gh_version.h is written by switch/build.sh into the build dir on every build.
    target_include_directories(gh PRIVATE ${GH_SWITCH_HOST}/include ${CMAKE_BINARY_DIR})
    target_link_options(gh PRIVATE
        -Wl,--wrap=__syscall_thread_create
        -Wl,-u,vk_icdGetInstanceProcAddr
    )
    # zstd, expat and zlib from devkitPro portlibs (the SDK links them on Linux through the system).
    # deko3d (libnx's native GPU API) for the deko3d backend (src/gpu/deko3d_backend.cpp).
    target_link_libraries(gh PRIVATE "${NVK_SDK}/lib/libvulkan.a" deko3d zstd expat z nx m stdc++)
    # nx_create_nro takes no NAME/AUTHOR/VERSION (it silently made a default NACP: "gh",
    # "Unspecified Author", 1.0.0 until 0.55): the NACP is generated here and handed to it.
    nx_generate_nacp(gh.nacp NAME "Guardian Heroes" AUTHOR "gh-recomp" VERSION "${GH_VERSION}")
    # The icon is the game's art: it stays local (git-ignored); without it the default icon is used.
    if(EXISTS "${CMAKE_SOURCE_DIR}/switch/icon/gh_icon.jpg")
        nx_create_nro(gh NACP gh.nacp ICON "${CMAKE_SOURCE_DIR}/switch/icon/gh_icon.jpg")
    else()
        nx_create_nro(gh NACP gh.nacp)
    endif()
endif()
