cmake_minimum_required(VERSION 3.20)
project(Ocuulo VERSION 0.1.0 LANGUAGES C CXX RC)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_STANDARD 11)

# Keep debug info even in Release so crash back-traces map to functions.
add_compile_options(/Zi)
add_link_options(/DEBUG)

# Phase 1: optimize for the smooth-framerate goal. (The earlier /Od was only to
# map a heap-corruption crash that is now fixed.) /O2 gives the decode + resample
# + relay paths the speed they need for sustained 60fps-on-motion.
add_compile_options(/O2)

# vcpkg toolchain expected via -DCMAKE_TOOLCHAIN_FILE, giving us OpenSSL, libplist, pthreads.
find_package(OpenSSL REQUIRED)
find_package(unofficial-libplist CONFIG REQUIRED)

set(VCPKG_INSTALLED "C:/vcpkg/installed/x64-windows")
# NOTE: no pthreads-win32 — we use a header-only Win32 pthread shim
# (src/airplay/uxplay_lib/pthread.h) because the prebuilt pthreadVC3.dll
# access-violated inside pthread_create on this toolchain.

get_filename_component(ROOT "${CMAKE_CURRENT_SOURCE_DIR}/.." ABSOLUTE)
set(UX "${ROOT}/src/airplay/uxplay_lib")

# ---- Sub-libraries bundled inside UxPlay's lib/ ----
file(GLOB PLAYFAIR_SRC "${UX}/playfair/*.c")
file(GLOB LLHTTP_SRC   "${UX}/llhttp/*.c")
set(MDNSD_SRC "${UX}/mdnsd/mdnsd.c" "${UX}/mdnsd/dnssd_mdnsd.c")

# ---- Core airplay (raop) library ----
file(GLOB AIRPLAY_SRC "${UX}/*.c")

add_library(airplay STATIC
    ${AIRPLAY_SRC}
    ${PLAYFAIR_SRC}
    ${LLHTTP_SRC}
    ${MDNSD_SRC}
)

target_include_directories(airplay PUBLIC
    ${UX}
    ${UX}/playfair
    ${UX}/llhttp
    ${UX}/mdnsd
    ${VCPKG_INSTALLED}/include
)

# WIN32 defines UxPlay expects, plus MSVC-friendliness and "use bundled mdnsd".
target_compile_definitions(airplay PUBLIC
    _WIN32 WIN32
    _CRT_SECURE_NO_WARNINGS
    _WINSOCK_DEPRECATED_NO_WARNINGS
    OPENSSL_API_COMPAT=0x10101000L
    HAVE_MDNS_EMBEDDED   # select bundled mdnsd backend in dnssd.c if it checks this
    PLIST_210            # our libplist is 2.7.0 -> use the >=2.1 plist_get_data_ptr API
    PLIST_230            # ...and >=2.3 provides plist_mem_free (skip UxPlay's local copy)
)

# Force-include the MSVC portability shim (winsock ordering, SOL_TCP, snprintf)
# before every airplay lib translation unit. Use the bare filename (UX is already
# an include dir) so the spaced project path doesn't break the /FI argument.
target_compile_options(airplay PRIVATE "/FImsvc_compat.h")
# Security hardening (2026-09-04, same as build_mirror.bat): stack cookies + Control Flow Guard
# on every object that lands in Ocuulo.exe, so the whole image is CFG-instrumented.
target_compile_options(airplay PRIVATE /GS /guard:cf)

target_link_libraries(airplay PUBLIC
    OpenSSL::Crypto OpenSSL::SSL
    unofficial::libplist::libplist
    ws2_32 iphlpapi winmm
)

# ---- Ocuulo app: AirPlay receiver + our media pipeline + branded Direct2D shell ----
# WIN32 GUI subsystem (no raw console): the branded OcuuloAirWindow is the UI, and
# the engine's stdout/stderr are routed to %LOCALAPPDATA%\Ocuulo\ios_wireless.log.
add_executable(Ocuulo
    ${ROOT}/src/airplay/AirPlayReceiver.cpp
    ${ROOT}/src/airplay/ocuulo_main.cpp
    ${ROOT}/src/airplay/OcuuloAirWindow.cpp
    ${ROOT}/src/app/MirrorWindow.cpp
    ${ROOT}/src/record/Mp4Recorder.cpp
    ${ROOT}/src/decode/H264Decoder.cpp
    ${ROOT}/src/decode/AudioDecoder.cpp
    ${ROOT}/src/render/D3D11Window.cpp
    ${ROOT}/src/audio/WasapiAudio.cpp
    ${ROOT}/src/ipc/SharedMem.cpp
    ${ROOT}/src/airplay/ocuulo_ios_wireless.rc
)

# rc.exe emits no dependency information for the raw binaries an RCDATA statement
# pulls in, so a re-baked .bgra would otherwise link into a stale .res while the
# generated *_dims.h told the C++ a NEW size — a silently skewed image. Declare
# the blobs explicitly so the resource recompiles whenever the art is re-baked.
set_source_files_properties(${ROOT}/src/airplay/ocuulo_ios_wireless.rc PROPERTIES
    OBJECT_DEPENDS "${ROOT}/src/airplay/ocuulo_ios_wireless.ico;${ROOT}/src/airplay/ocuulo_titlebar_lockup.bgra;${ROOT}/src/airplay/phone_stage_1.bgra;${ROOT}/src/airplay/phone_stage_2.bgra;${ROOT}/src/airplay/phone_stage_3.bgra"
)

# GUI subsystem + wide entry point (wWinMain). WIN32_EXECUTABLE sets /SUBSYSTEM:WINDOWS;
# MSVC's default WINDOWS entry is WinMainCRTStartup, so force the wide variant.
set_target_properties(Ocuulo PROPERTIES WIN32_EXECUTABLE TRUE)

target_include_directories(Ocuulo PRIVATE
    ${ROOT}/src
    C:/vcpkg/installed/x64-windows/include
)

target_compile_definitions(Ocuulo PRIVATE USE_D3D11_RENDERER=1 NOMINMAX WIN32_LEAN_AND_MEAN)

# Our sources are UTF-8 without a BOM. Without /utf-8 MSVC decodes non-ASCII
# literals as the ANSI codepage, which turned a "·" in a wide string literal into
# "Â·" on screen. (Escapes like · are still preferred in UI strings.)
target_compile_options(Ocuulo PRIVATE /utf-8)
# Security hardening (2026-09-04, same flags as build_mirror.bat's shipping exes): /GS stack
# cookies + /guard:cf; the link side adds CFG, CET shadow stack, ASLR (high entropy) and DEP
# below. Ocuulo.exe was the one shipping exe with none of these (dumpbin: DLL chars 0x8160).
target_compile_options(Ocuulo PRIVATE /GS /guard:cf)

# FFmpeg (installed in space-free vcpkg per earlier work)
find_library(AVCODEC avcodec PATHS "${VCPKG_INSTALLED}/lib")
find_library(AVUTIL  avutil  PATHS "${VCPKG_INSTALLED}/lib")
find_library(SWSCALE swscale PATHS "${VCPKG_INSTALLED}/lib")
find_library(SWRESAMPLE swresample PATHS "${VCPKG_INSTALLED}/lib")
find_library(AVFORMAT avformat PATHS "${VCPKG_INSTALLED}/lib")   # HUD Record -> MP4 mux (Mp4Recorder)

target_link_libraries(Ocuulo PRIVATE
    airplay
    ${AVCODEC} ${AVUTIL} ${SWSCALE} ${SWRESAMPLE} ${AVFORMAT}
    d3d11 dxgi d3dcompiler
    d2d1 dwrite dwmapi   # branded Direct2D/DirectWrite shell + borderless custom chrome
    user32 gdi32
    ole32 avrt
    ws2_32 iphlpapi
)
# wWinMain entry (WIN32_EXECUTABLE only picks the WINDOWS subsystem, not the wide entry).
target_link_options(Ocuulo PRIVATE "/ENTRY:wWinMainCRTStartup" "/MAP:${CMAKE_BINARY_DIR}/Ocuulo.map")
# Security hardening link flags (2026-09-04) + /PDBALTPATH so the shipped exe embeds only the
# PDB file name instead of this machine's full build path (the /DEBUG above kept the CodeView
# record; the path inside it is an information leak, the debug info itself is not).
target_link_options(Ocuulo PRIVATE /guard:cf /CETCOMPAT /DYNAMICBASE /HIGHENTROPYVA /NXCOMPAT "/PDBALTPATH:%_PDB%")
