cmake_minimum_required(VERSION 3.16)
project(tokyowar C)

if(NOT CMAKE_C_COMPILER_ID STREQUAL "GNU")
  message(FATAL_ERROR "gcc required (the lifted code uses computed labels and statement expressions)")
endif()
if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE RelWithDebInfo)
endif()
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_EXTENSIONS ON)
add_compile_options(-fno-strict-aliasing -fwrapv)

# PORTABLE (default ON): pin glibc symbol versions so a binary built here runs on older systems -- the Steam Deck included.
# (Prop Cycle's ../include/glibc_compat.h: one copy for every game.)
option(PORTABLE "Bind glibc symbols to baseline versions (runs on glibc >= 2.34)" ON)
if(PORTABLE AND NOT WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
  add_compile_options(-include ${CMAKE_CURRENT_SOURCE_DIR}/../include/glibc_compat.h)
endif()

include_directories(include gen)

# THE SHARED ENGINE (../engine): the 68K host runtime (lift_cpu.c) today; the renderer, mixer
# and DSP translation when T4-T6 land (HARD RULE 3 in ../CLAUDE.md).
include(${CMAKE_CURRENT_SOURCE_DIR}/../engine/engine.cmake)

# gen/tw_lifted_NN.c + gen/tw_lifted_tab.c: the 68K program, GENERATED by ../tools/namco22/lift.py from decompiled/pcode.txt and SPLIT by the
# shared lift_split.py (as Time Crisis):
#   python3 ../tools/namco22/lift.py decompiled/pcode.txt gen tw && python3 ../tools/namco22/lift_split.py gen/tw_lifted.c 16
# One 23-27 MB file was a single gcc process on one core for minutes (three times per release); 16 parts compile in parallel.
# Big goto-heavy C; -O1 keeps the build short and is plenty for a 24 MHz 68EC020.
file(GLOB TW_LIFTED CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/gen/tw_lifted_*.c)
if(NOT TW_LIFTED)
  if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/gen/tw_lifted.c)
    message(FATAL_ERROR "gen/tw_lifted.c is not split: run  python3 ../tools/namco22/lift_split.py gen/tw_lifted.c 16")
  endif()
  message(FATAL_ERROR "gen/tw_lifted_NN.c missing (the lifted 68K program)")
endif()
set_source_files_properties(${TW_LIFTED} PROPERTIES COMPILE_OPTIONS "-O1;-w")

# The MASTER DSP program (the C71 BIOS + the block the 68K uploads through it), translated to C from
# the ROM files by the shared tool ../tools/gen/c25_translate.py. tools/gen/c25.cov: the addresses
# the interpreter oracle executed (addresses only, no ROM data; committed). The game has no C25
# fetch/decode loop. Block 0x127816 is the master program (FUN_0012ED28 uploads it as block 1).
# --block: also the TEST-MODE master programs (2026-10-05, GitHub #26): the 68K's table at ROM 0x125250 (12 entries; the same
# Namco board tests as Time Crisis's 0xB91C4, 7 of them word for word) and 0x12C070 (uploaded by 0x124C52). Without them the
# translated master stopped in test mode (no translation at 400B..413E).
find_package(Python3 REQUIRED COMPONENTS Interpreter)
add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/gen/tw_c25.c
  COMMAND ${Python3_EXECUTABLE} ../tools/gen/c25_translate.py --game tw --roms extracted
          --block 127816 --block 125280 --block 125284 --block 12529E --block 12543A --block 1255C2 --block 1256B2
          --block 1258B4 --block 125D5E --block 125DEC --block 1261CA --block 126630 --block 126EC2 --block 12C070
          --cov tools/gen/c25.cov --out gen/tw_c25.c --func tw_c25_exec
  DEPENDS ../tools/gen/c25_translate.py tools/gen/c25.cov ../engine/c25/c71_bios.inc
  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
  COMMENT "Translating the master DSP program to C")
set_source_files_properties(gen/tw_c25.c PROPERTIES COMPILE_OPTIONS "-O2;-w")

# The SOUND MCU program (tw1data.8k -- the S22-BIOS ver1.41 and the game's sound program, which also
# reads the cabinet's ports), translated to C from the ROM file by the shared tools/gen/snd_translate.py
# (--game tw). tools/gen/snd.cov: the (address, M/X, length) of every instruction the tw_oracle test
# build executed (addresses only, no ROM data; committed). The game has no M37710 fetch/decode loop.
add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/gen/tw_snd_driver.c
  COMMAND ${Python3_EXECUTABLE} ../tools/gen/snd_translate.py tools/gen/snd.cov --game tw --roms extracted
          --out gen/tw_snd_driver.c
  DEPENDS ../tools/gen/snd_translate.py ../tools/gen/snd_rom.py ../tools/gen/snd_spin.inc ../tools/gen/snd_dispatch.py ../tools/gen/snd_indirect.py ../tools/gen/snd_exits.py ../tools/gen/m7700_decode.py tools/gen/snd.cov
  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
  COMMENT "Translating the sound MCU program to C")
set_source_files_properties(gen/tw_snd_driver.c PROPERTIES COMPILE_OPTIONS "-O1;-w")

# the game: the 68K (lifted), the master DSP (translated), the sound MCU (translated) and its C352
# the picture and the window: the shared engine's geometry stage, texture bake, GL rasteriser and Super 22 layers
# (ss22_gl.c), around Tokyo Wars' own video RAM (tw_video.c), controls (tw_input.c) and window (tw_host.c)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
if(WIN32)
  set(ZLIB_USE_STATIC_LIBS ON)                    # MSYS2's zlib comes as a DLL too: link the static one, or the .exe needs zlib1.dll (CMake 3.24+)
endif()
find_package(ZLIB REQUIRED)                       # the first-run ROM unpacker (engine/romzip.c)
set(OpenGL_GL_PREFERENCE GLVND)
find_package(OpenGL REQUIRED)
include_directories(${SDL2_INCLUDE_DIRS})
# Windows (MinGW-w64, ../build-windows.sh): the few POSIX calls mapped in ../include/win_compat.h, no console window, and
# everything linked in -- SDL2 statically too -- so TokyoWars.exe needs no DLL beside it. OpenGL is NOT linked: the shared
# engine's gl_dyn.c resolves it at run time, so the bundled Mesa can stand in when Windows has no real driver.
if(WIN32)
  add_compile_options(-include ${CMAKE_CURRENT_SOURCE_DIR}/../include/win_compat.h)
  add_link_options(-mwindows -static-libgcc -static)
  set(SDL2_LIBRARIES ${SDL2_STATIC_LDFLAGS} ws2_32)   # engine/ss22_out.c: the MAME network-output socket (Winsock2)
  set(TW_GL_LIB "")
  set(TW_GL_SRC ${NAMCO22_ENGINE_DIR}/gl_dyn.c)
else()
  set(TW_GL_LIB OpenGL::GL)
  set(TW_GL_SRC "")
endif()

set(TW_CORE_SRC src/tw_game.c src/tw_net.c ${NAMCO22_ENGINE_NET_SRC}
    ${NAMCO22_ENGINE_LIFT_SRC} ${NAMCO22_ENGINE_C25_SRC} ${NAMCO22_ENGINE_SND_SRC}
    ${NAMCO22_ENGINE_GL_SRC} ${NAMCO22_ENGINE_SS22_SRC} ${NAMCO22_ENGINE_SS22_HOST_SRC} ${NAMCO22_ENGINE_UI_SRC} ${NAMCO22_ENGINE_ROMZIP_SRC} ${TW_GL_SRC}
    gen/tw_c25.c ${TW_LIFTED})
set(TW_CPU_SRC ${TW_CORE_SRC} gen/tw_snd_driver.c)

add_executable(tw ${TW_CPU_SRC})
target_compile_options(tw PRIVATE -Wall)
target_link_libraries(tw ${SDL2_LIBRARIES} ${TW_GL_LIB} ZLIB::ZLIB m)

# Development builds (the instruction trace and the translation gate): only when their sources are present -- the public tree
# has just the game.
if(EXISTS ${NAMCO22_TOOLS_DIR}/sndoracle/sound_oracle.c)
# instruction-trace build: every instruction logs PC + D0-D7/A0-A7
add_executable(tw_trace ${TW_CPU_SRC})
target_compile_options(tw_trace PRIVATE -Wall)
target_compile_definitions(tw_trace PRIVATE RR_TRACE)
target_link_libraries(tw_trace ${SDL2_LIBRARIES} OpenGL::GL ZLIB::ZLIB m)

# THE TRANSLATION GATE / COVERAGE BUILD (dev only, never shipped -- HARD RULE 1): the same host with
# the interpreter oracle linked in. TW_C25=oracle runs the program through it (C71_COV=file records
# the addresses it executes); TW_C25=lockstep runs the translation with a shadow master on the
# oracle beside it and requires their state equal after every slice (tools/c25_gate.sh).
# (the sound program runs through tools/sndoracle's fetch/decode loop here instead of the translation:
#  SND_COV=<file> records the instructions it executes, the translator's input)
add_executable(tw_oracle ${TW_CORE_SRC} ${NAMCO22_C25_ORACLE_SRC}
    ${NAMCO22_TOOLS_DIR}/sndoracle/m37710.c ${NAMCO22_TOOLS_DIR}/sndoracle/sound_oracle.c)
target_compile_options(tw_oracle PRIVATE -Wall)
target_compile_definitions(tw_oracle PRIVATE TW_ORACLE SS22_ORACLE C25_DEV_HOOKS)
target_link_libraries(tw_oracle ${SDL2_LIBRARIES} OpenGL::GL ZLIB::ZLIB m)
endif()
