# mp3-ps4 - Max Payne 3 (Xbox 360, Title Update 6) on the PS4 (ps4-kit: clang 21, OpenOrbis, x86-64 Jaguar).
#
# The MGR port's way (iHaiDeeZ/MGR-PS4), not the Switch build's: the game's PowerPC code recompiled to C++ and built
# for x86-64 (no ARM pass: tools/codegen.sh with BARRIERS=0), and the game's command ring drawn by the SDK's own Xenos
# emulation on its Vulkan back end, on ps4-kit's statically linked RADV, instead of deko3d. One eboot.
#
# Configure (scripts/build-mp3.sh app does it):
#   ps4_cmake -S mp3/ps4 -B <build> -G Ninja -DCMAKE_BUILD_TYPE=Release \
#     -DCMAKE_PROJECT_mp3_INCLUDE=$PS4_KIT/ps4-kit.cmake -DPS4_KIT_NETFS=OFF \
#     -DREXSDK_DIR=$PWD/sdk -DMP3_CODEGEN_DIR=$PWD/mp3/out/codegen -DREXGLUE_PLATFORM_PS4=ON -DREXGLUE_USE_VULKAN=ON

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

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

if(NOT REXGLUE_PLATFORM_PS4)
    message(FATAL_ERROR "mp3/ps4: the PS4 build (-DREXGLUE_PLATFORM_PS4=ON, ps4-kit's toolchain)")
endif()

get_filename_component(MP3_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.." ABSOLUTE)

set(MP3_CODEGEN_DIR "" CACHE PATH "tools/codegen.sh output folder (holds generated/default/)")
set(MP3_GENERATED "${MP3_CODEGEN_DIR}/generated/default")
if(NOT EXISTS "${MP3_GENERATED}/sources.cmake")
    message(FATAL_ERROR
        "MP3_CODEGEN_DIR must point at tools/codegen.sh's output "
        "(no generated/default/sources.cmake under '${MP3_CODEGEN_DIR}')")
endif()

set(REXSDK_DIR "" CACHE PATH "Path to the rexglue-sdk(-nx) source tree")
if(NOT REXSDK_DIR)
    get_filename_component(REXSDK_DIR "${MP3_DIR}/../sdk" ABSOLUTE)
endif()
add_subdirectory("${REXSDK_DIR}" rexglue-sdk)
message(STATUS "mp3: ReXGlue SDK from ${REXSDK_DIR}")

# The mirror of the generated tree with the PS4 PCH (tools/make_ps4_pch.py).
find_package(Python3 3.10 REQUIRED COMPONENTS Interpreter)
set(MP3_TREE "${CMAKE_BINARY_DIR}/generated_ps4")
execute_process(
    COMMAND "${Python3_EXECUTABLE}" -I "${MP3_DIR}/tools/prepare_tree.py"
            "${MP3_GENERATED}" "${MP3_TREE}" "${MP3_DIR}/tools/make_ps4_pch.py"
    RESULT_VARIABLE _mp3_tree_rc)
if(NOT _mp3_tree_rc EQUAL 0)
    message(FATAL_ERROR "mp3: tools/prepare_tree.py failed (${_mp3_tree_rc})")
endif()
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
    "${MP3_GENERATED}/mp3_pch.h"
    "${MP3_GENERATED}/sources.cmake"
    "${MP3_DIR}/tools/make_nx_pch.py"
    "${MP3_DIR}/tools/make_ps4_pch.py"
    "${MP3_DIR}/tools/prepare_tree.py")

# generated/default/sources.cmake: the support units and the recompiled ones. CMAKE_CURRENT_LIST_DIR there is the mirror.
include("${MP3_TREE}/sources.cmake")
set(MP3_GUEST_SOURCES ${GENERATED_SOURCES})
# No debug info for the ~270 generated units (they would take gigabytes), the guest's floating point unfused (the
# Jaguar has no FMA unit, and the console rounds each step), no strict aliasing (guest memory is read through any
# type), as the MGR port builds its generated code.
set(MP3_RECOMP_OPTIONS -g0 -ffp-contract=off -fno-strict-aliasing)
# The registration unit is one function of ~37,000 SetFunction calls, run once at startup: -O1 and no PCH (Switch
# build's reason, kept).
set_source_files_properties("${MP3_TREE}/mp3_register.cpp" PROPERTIES
    COMPILE_OPTIONS "-O1"
    SKIP_PRECOMPILE_HEADERS ON)
list(LENGTH MP3_GUEST_SOURCES _mp3_n)
message(STATUS "mp3: ${_mp3_n} recompiled sources from ${MP3_GENERATED}")

add_executable(mp3
    main_ps4.cpp
    mp3_ps4.cpp
    mp3_ps4_fps.cpp
    mp3_ps4_menu.cpp
    mp3_ps4_watchdog.cpp
    mp3_ps4_live.cpp
    # Portable parts of the Switch build (src/): the Games on Demand package, the per-thread interrupt locks,
    # the recompiled code's diagnostics, the game's spin-lock and Direct3D wait hooks (each only when
    # codegen/names.toml names its function), the detail settings.
    ${MP3_DIR}/src/mp3_god.cpp
    ${MP3_DIR}/src/mp3_cpu.cpp
    ${MP3_DIR}/src/mp3_guards.cpp
    ${MP3_DIR}/src/mp3_perf_hooks.cpp
    ${MP3_DIR}/src/mp3_detail.cpp
)

add_library(mp3_recomp OBJECT ${MP3_GUEST_SOURCES})
target_include_directories(mp3_recomp PRIVATE "${MP3_TREE}" "${MP3_DIR}/src")
target_link_libraries(mp3_recomp PRIVATE rex::runtime)
rexglue_apply_target_settings(mp3_recomp)
target_precompile_headers(mp3_recomp PRIVATE "${MP3_TREE}/mp3_pch.h")
target_compile_options(mp3_recomp PRIVATE ${MP3_RECOMP_OPTIONS})
target_link_libraries(mp3 PRIVATE mp3_recomp)
target_include_directories(mp3 PRIVATE "${MP3_TREE}" "${MP3_DIR}/src" "${CMAKE_CURRENT_SOURCE_DIR}")
target_link_libraries(mp3 PRIVATE rex::runtime)
target_compile_options(mp3 PRIVATE -ffp-contract=off -fno-strict-aliasing)

# GTA IV's hooks are compiled only for the names the codegen was given (MP3_TIENE_<name>), as in the Switch build.
# MP3_NAMES_TOML: the names file tools/codegen.sh used (CODEGEN_NAMES; the PS4 port's own for the 0.0.6.6 executable).
set(MP3_NAMES_TOML "${MP3_DIR}/codegen/names.toml" CACHE FILEPATH "The names.toml the codegen used")
file(STRINGS "${MP3_NAMES_TOML}" MP3_NOMBRES REGEX "^name = \"mp3_[A-Za-z0-9_]+\"")
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${MP3_NAMES_TOML}")
set(MP3_OWN_SOURCES ${MP3_DIR}/src/mp3_perf_hooks.cpp ${MP3_DIR}/src/mp3_detail.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/mp3_ps4_fps.cpp)
foreach(linea IN LISTS MP3_NOMBRES)
    string(REGEX REPLACE "^name = \"(mp3_[A-Za-z0-9_]+)\".*" "\\1" nombre "${linea}")
    set_property(SOURCE ${MP3_OWN_SOURCES} APPEND PROPERTY COMPILE_DEFINITIONS MP3_TIENE_${nombre}=1)
endforeach()

# The version and the commit it was built from, in the first lines of the log (src/mp3_cpu.cpp).
set(MP3_VERSION "ps4-0.1" CACHE STRING "Version in the log")
execute_process(COMMAND git rev-parse --short=10 HEAD WORKING_DIRECTORY "${MP3_DIR}"
                OUTPUT_VARIABLE MP3_COMMIT OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
if(NOT MP3_COMMIT)
    set(MP3_COMMIT "unknown")
endif()
set_property(SOURCE ${MP3_DIR}/src/mp3_cpu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mp3_ps4_menu.cpp APPEND PROPERTY
             COMPILE_DEFINITIONS MP3_VERSION="${MP3_VERSION}" MP3_COMMIT="${MP3_COMMIT}")
# The quick menu's Lato fonts are #embed'ed (fonts/, SIL Open Font License: fonts/LICENSE-Lato.txt).
set_property(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/mp3_ps4_menu.cpp APPEND PROPERTY OBJECT_DEPENDS
             ${CMAKE_CURRENT_SOURCE_DIR}/fonts/Lato-Regular.ttf ${CMAKE_CURRENT_SOURCE_DIR}/fonts/Lato-Black.ttf)

# The runtime, ReXApp and the PS4 entry point, with the Xenos GPU (Vulkan) linked whole; RADV and the kit's WSI; the
# eboot.
rexglue_configure_target(mp3 GPU_PLUGINS xenos)
ps4_kit_link_mesa(mp3)
ps4_kit_app(mp3)
# The kit's DNS wrap (ps4_net.cpp) without its SMB/NFS shares (PS4_KIT_NETFS off): the libc member has to be pulled in
# by name, or __real_getaddrinfo stays an import create-fself refuses (ConkerLR's and MGR's fix).
target_link_options(mp3 PRIVATE --undefined=getaddrinfo)
