cmake_minimum_required(VERSION 3.20)
project(cb2_viewer CXX C)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(FetchContent)
set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
FetchContent_Declare(glfw GIT_REPOSITORY https://github.com/glfw/glfw.git GIT_TAG 3.4)
FetchContent_Declare(glm GIT_REPOSITORY https://github.com/g-truc/glm.git GIT_TAG 1.0.1)
FetchContent_MakeAvailable(glfw glm)

add_library(glad STATIC third_party/glad/src/gl.c)
target_include_directories(glad PUBLIC third_party/glad/include)

# Native engine code: generated by tools/recomp.py from the executable into build/gen (never committed).
file(GLOB CB2_GEN_SOURCES CONFIGURE_DEPENDS ${CMAKE_BINARY_DIR}/gen/*.cpp)
if(CB2_GEN_SOURCES)
  set(CB2_RECOMP_SOURCES ${CB2_GEN_SOURCES})
else()
  set(CB2_RECOMP_SOURCES src/recomp_stub.cpp)
endif()
add_library(cb2 STATIC src/nsf.cpp src/nsd.cpp src/bundle.cpp src/net.cpp src/directory.cpp src/multiplayer.cpp src/props.cpp src/propasset.cpp src/entity.cpp src/model.cpp src/scenery.cpp src/scenery_edit.cpp src/gool.cpp src/gool_decode.cpp src/gool_asm.cpp src/gool_vm.cpp src/gool_native.cpp src/r3000.cpp src/gte.cpp src/gpu.cpp src/game_profile.cpp src/disc.cpp src/cdrom.cpp src/spu.cpp src/memcard.cpp src/psx.cpp src/savestate.cpp ${CB2_RECOMP_SOURCES})
target_include_directories(cb2 PUBLIC src ${CMAKE_BINARY_DIR}/gen)
if(MSVC)
  target_compile_options(cb2 PRIVATE /bigobj)
  if(CB2_GEN_SOURCES)
    set_source_files_properties(${CB2_GEN_SOURCES} PROPERTIES COMPILE_OPTIONS "/W0;/MP")
  endif()
endif()

add_executable(cb2_check src/check.cpp)
target_link_libraries(cb2_check PRIVATE cb2)

add_executable(cb2_roundtrip src/roundtrip.cpp)
target_link_libraries(cb2_roundtrip PRIVATE cb2)

add_executable(cb2_mod_demo src/mod_demo.cpp)
target_link_libraries(cb2_mod_demo PRIVATE cb2)

add_executable(cb2_cpurun src/cpurun.cpp)
target_link_libraries(cb2_cpurun PRIVATE cb2)

add_executable(cb2_gtetest src/gtetest.cpp)
target_link_libraries(cb2_gtetest PRIVATE cb2)

add_executable(cb2_scene src/scene_tool.cpp)
add_executable(cb2_entities src/entity_tool.cpp)
target_link_libraries(cb2_entities PRIVATE cb2)
add_executable(cb2_models src/model_tool.cpp)
add_executable(cb2_server src/server.cpp)
if(EXISTS "${CMAKE_SOURCE_DIR}/local/directory.txt")
  file(STRINGS "${CMAKE_SOURCE_DIR}/local/directory.txt" CB2_SERVER_DIRECTORY LIMIT_COUNT 1)
  string(STRIP "${CB2_SERVER_DIRECTORY}" CB2_SERVER_DIRECTORY)
  if(CB2_SERVER_DIRECTORY)
    target_compile_definitions(cb2_server PRIVATE "CB2_DEFAULT_DIRECTORY=\"${CB2_SERVER_DIRECTORY}\"")   # the server lists itself in the same lobby service the game uses
  endif()
  set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/local/directory.txt")
endif()
add_executable(cb2_runtest src/run_test.cpp src/runclock.cpp src/records.cpp)
target_link_libraries(cb2_runtest PRIVATE cb2)
add_executable(cb2_rostertest src/roster_test.cpp src/roster.cpp)
target_link_libraries(cb2_rostertest PRIVATE cb2)
add_executable(cb2_worldprobe src/world_probe.cpp)
target_link_libraries(cb2_worldprobe PRIVATE cb2)
add_executable(cb2_interptest src/interp_test.cpp src/interp.cpp)
target_link_libraries(cb2_interptest PRIVATE cb2)
add_executable(cb2_spureverbtest src/spu_test.cpp)
target_link_libraries(cb2_spureverbtest PRIVATE cb2)
add_executable(cb2_discbench src/disc_bench.cpp)
target_link_libraries(cb2_discbench PRIVATE cb2)
add_executable(cb2_cardfind src/card_find.cpp)
target_link_libraries(cb2_cardfind PRIVATE cb2)
add_executable(cb2_scan src/scan_tool.cpp)
target_link_libraries(cb2_scan PRIVATE cb2)
add_executable(cb2_statediff src/state_diff.cpp)
target_link_libraries(cb2_statediff PRIVATE cb2)
add_executable(cb2_statetest src/state_test.cpp)
target_link_libraries(cb2_statetest PRIVATE cb2)
add_executable(cb2_nettest src/net_test.cpp)
target_link_libraries(cb2_nettest PRIVATE cb2)
add_executable(cb2_importtest src/import_test.cpp src/discimport.cpp)
target_link_libraries(cb2_importtest PRIVATE cb2)
add_executable(cb2_mpspike src/mpspike.cpp)
target_link_libraries(cb2_mpspike PRIVATE cb2)
add_executable(cb2_crashtrace src/crashtrace.cpp)
target_link_libraries(cb2_crashtrace PRIVATE cb2)
target_link_libraries(cb2_models PRIVATE cb2)
target_link_libraries(cb2_scene PRIVATE cb2)

add_executable(cb2_gool src/gool_tool.cpp)
target_link_libraries(cb2_gool PRIVATE cb2)

add_executable(cb2_memtest src/memtest.cpp)
target_link_libraries(cb2_memtest PRIVATE cb2)

add_executable(cb2_nativecheck src/nativecheck.cpp)
target_link_libraries(cb2_nativecheck PRIVATE cb2)

add_executable(cb2_boot src/boot.cpp)
target_link_libraries(cb2_boot PRIVATE cb2)

add_executable(cb2_vmrun src/vmrun.cpp)
target_link_libraries(cb2_vmrun PRIVATE cb2)

add_executable(cb2_vmdiff src/vmdiff.cpp)
target_link_libraries(cb2_vmdiff PRIVATE cb2)

add_executable(cb2_viewer src/main.cpp)
target_include_directories(cb2_viewer PRIVATE third_party)
target_link_libraries(cb2_viewer PRIVATE cb2 glad glfw glm::glm)

# The address of the lobby service is not part of the source: put it in local/directory.txt (git-ignored) and it is built into the game, so
# players never type it. Same for local/rendezvous.txt (an optional relay server). Without the files the game starts with no lobby service.
foreach(kind directory rendezvous)
  set(file "${CMAKE_SOURCE_DIR}/local/${kind}.txt")
  string(TOUPPER ${kind} KIND)
  if(EXISTS "${file}")
    file(STRINGS "${file}" value LIMIT_COUNT 1)
    string(STRIP "${value}" value)
    if(value)
      list(APPEND CB2_DEFAULT_DEFS "CB2_DEFAULT_${KIND}=\"${value}\"")
    endif()
    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${file}")
  endif()
endforeach()
# The version comes from the VERSION file (tag the GitHub release v<VERSION>); the running game compares it with the latest release. See docs/RELEASING.md.
file(STRINGS "${CMAKE_SOURCE_DIR}/VERSION" CB2_VERSION LIMIT_COUNT 1)
string(STRIP "${CB2_VERSION}" CB2_VERSION)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/VERSION")
# the PUBLIC repository whose releases the updater reads and whose issue page the bug report button opens (docs/WEBSITE.md)
set(CB2_RELEASE_REPO "ignzio/CB2-Online" CACHE STRING "owner/name of the public release repository")
list(APPEND CB2_DEFAULT_DEFS "CB2_VERSION=\"${CB2_VERSION}\"" "CB2_UPDATE_REPO=\"${CB2_RELEASE_REPO}\"")
add_executable(cb2_play src/play.cpp src/audio.cpp src/glrender.cpp src/nativeworld.cpp src/crashreport.cpp src/config.cpp src/lobby.cpp src/ui.cpp src/keymap.cpp src/pausemenu.cpp src/relaunch.cpp src/updater.cpp src/discimport.cpp src/setup.cpp src/roster.cpp src/hud.cpp src/runclock.cpp src/records.cpp src/runtracker.cpp src/interp.cpp)
target_include_directories(cb2_play PRIVATE third_party)
target_link_libraries(cb2_play PRIVATE cb2 glad glfw)
target_compile_definitions(cb2_play PRIVATE ${CB2_DEFAULT_DEFS})
if(WIN32)
  enable_language(RC)
  # Version information for both programs (name, version, licence, source address): unsigned programs without it are far more likely to be flagged by Windows Defender / SmartScreen.
  string(REPLACE "." ";" CB2_VER_LIST "${CB2_VERSION}")
  list(LENGTH CB2_VER_LIST CB2_VER_N)
  while(CB2_VER_N LESS 4)
    list(APPEND CB2_VER_LIST 0)
    math(EXPR CB2_VER_N "${CB2_VER_N} + 1")
  endwhile()
  string(REPLACE ";" "," CB2_VER_COMMA "${CB2_VER_LIST}")
  file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/gen")
  set(CB2_RC_DESCRIPTION "Crash Bandicoot 2 native PC port with online multiplayer")
  set(CB2_RC_INTERNAL "Crash2")
  set(CB2_RC_ORIGINAL "Crash2.exe")
  configure_file(assets/version.rc.in "${CMAKE_BINARY_DIR}/gen/cb2_version.rc" @ONLY)
  set(CB2_RC_DESCRIPTION "CB2-Online rendezvous server (relays nothing but small position packets)")
  set(CB2_RC_INTERNAL "Crash2-Server")
  set(CB2_RC_ORIGINAL "Crash2-Server.exe")
  configure_file(assets/version.rc.in "${CMAKE_BINARY_DIR}/gen/cb2_server_version.rc" @ONLY)
  target_sources(cb2_server PRIVATE assets/cb2_server.rc)
  target_include_directories(cb2_server PRIVATE assets "${CMAKE_BINARY_DIR}/gen")
  # The single-file game: cb2_play as a windowed program (no console) with the icon. tools/make_exe.ps1 appends the game data.
  add_executable(cb2_game WIN32 src/play.cpp src/audio.cpp src/glrender.cpp src/nativeworld.cpp src/crashreport.cpp src/config.cpp src/lobby.cpp src/ui.cpp src/keymap.cpp src/pausemenu.cpp src/relaunch.cpp src/updater.cpp src/discimport.cpp src/setup.cpp src/roster.cpp src/hud.cpp src/runclock.cpp src/records.cpp src/runtracker.cpp src/interp.cpp assets/cb2.rc)
  target_include_directories(cb2_game PRIVATE third_party assets "${CMAKE_BINARY_DIR}/gen")
  target_link_libraries(cb2_game PRIVATE cb2 glad glfw)
  target_compile_definitions(cb2_game PRIVATE ${CB2_DEFAULT_DEFS})
  set_target_properties(cb2_game PROPERTIES LINK_FLAGS "/ENTRY:mainCRTStartup /MAP")   # the link map lets tools/resolve_crash.py turn the addresses of a crash report into function names
endif()
add_executable(cb2_pack src/pack_tool.cpp)
target_link_libraries(cb2_pack PRIVATE cb2)

if(MSVC)
  target_compile_options(cb2 PRIVATE /W4)
  target_compile_options(cb2_viewer PRIVATE /W4)
endif()
