diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 00000000..94186f4b
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,71 @@
+cmake_minimum_required(VERSION 3.18)
+
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
+include(identity)
+
+project(${PROJECT_NAME} VERSION ${PROJECT_VERSION} LANGUAGES C ASM)
+
+option(BUILD_SERVER "Build dedicated server" ON)
+option(BUILD_CLIENT "Build client" ON)
+option(BUILD_RENDERER_GL1 "Build GL1 renderer" ON)
+option(BUILD_RENDERER_GL2 "Build GL2 renderer" ON)
+option(BUILD_GAME_LIBRARIES "Build game module libraries" ON)
+option(BUILD_GAME_QVMS "Build game module qvms" ON)
+option(BUILD_STANDALONE "Build binaries for standalone games" OFF)
+
+option(USE_ARCHLESS_FILENAMES "Don't include the architecture in binary filenames" OFF)
+option(USE_RENDERER_DLOPEN "Dynamically load the renderer(s)" ON)
+option(USE_OPENAL "OpenAL audio" ON)
+option(USE_OPENAL_DLOPEN "Dynamically load OpenAL" ON)
+option(USE_HTTP "HTTP download support" ON)
+option(USE_CODEC_VORBIS "Ogg Vorbis support" ON)
+option(USE_CODEC_OPUS "Ogg Opus support" ON)
+option(USE_VOIP "Voice chat" ON)
+option(USE_MUMBLE "Mumble support" ON)
+option(USE_FREETYPE "Freetype font rendering" OFF)
+
+option(USE_INTERNAL_LIBS "Use internally packaged libraries" ON)
+option(USE_INTERNAL_ZLIB "Use internal copy of zlib" ${USE_INTERNAL_LIBS})
+option(USE_INTERNAL_JPEG "Use internal copy of libjpeg" ${USE_INTERNAL_LIBS})
+option(USE_INTERNAL_OGG "Use internal copy of ogg" ${USE_INTERNAL_LIBS})
+option(USE_INTERNAL_VORBIS "Use internal copy of vorbis" ${USE_INTERNAL_LIBS})
+option(USE_INTERNAL_OPUS "Use internal copy of opus" ${USE_INTERNAL_LIBS})
+
+# Release build by default, set externally if you want something else
+if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE Release)
+endif()
+
+set(CMAKE_C_STANDARD 99)
+set(CMAKE_C_STANDARD_REQUIRED ON)
+set(CMAKE_SHARED_LIBRARY_PREFIX "")
+
+set(PRODUCT_VERSION "${CMAKE_PROJECT_VERSION}")
+
+if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
+ execute_process(
+ COMMAND git show -s --pretty=format:%h
+ WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+ OUTPUT_VARIABLE GIT_REV
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ ERROR_QUIET)
+
+ if(GIT_REV)
+ set(PRODUCT_VERSION "${PRODUCT_VERSION}_g${GIT_REV}")
+ endif()
+endif()
+
+add_compile_definitions(PRODUCT_VERSION="${PRODUCT_VERSION}")
+
+set(SOURCE_DIR ${CMAKE_SOURCE_DIR}/code)
+
+include(compilers/all)
+include(platforms/all)
+include(libraries/all)
+
+include(server)
+include(renderer_gl1)
+include(renderer_gl2)
+include(client)
+include(basegame)
+include(missionpack)
diff --git a/cmake/Info.plist.in b/cmake/Info.plist.in
new file mode 100644
index 00000000..d52ade89
--- /dev/null
+++ b/cmake/Info.plist.in
@@ -0,0 +1,34 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ @MACOS_APP_EXECUTABLE_NAME@
+ CFBundleIconFile
+ @MACOS_APP_ICON_FILE@
+ CFBundleIdentifier
+ @MACOS_APP_GUI_IDENTIFIER@
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ @MACOS_APP_BUNDLE_NAME@
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ @MACOS_APP_SHORT_VERSION_STRING@
+ CFBundleVersion
+ @MACOS_APP_BUNDLE_VERSION@
+ NSHumanReadableCopyright
+ @MACOS_APP_COPYRIGHT@
+ NSPrincipalClass
+ NSApplication
+ LSMinimumSystemVersion
+ @MACOS_APP_DEPLOYMENT_TARGET@
+ LSApplicationCategoryType
+ public.app-category.games
+
+ @MACOS_APP_PLIST_URL_TYPES@
+
+
diff --git a/cmake/basegame.cmake b/cmake/basegame.cmake
new file mode 100644
index 00000000..946de095
--- /dev/null
+++ b/cmake/basegame.cmake
@@ -0,0 +1,184 @@
+if(NOT BUILD_GAME_LIBRARIES AND NOT BUILD_GAME_QVMS)
+ return()
+endif()
+
+include(utils/arch)
+include(utils/qvm_tools)
+include(utils/set_output_dirs)
+
+set(CGAME_SOURCES
+ ${SOURCE_DIR}/cgame/cg_main.c
+ ${SOURCE_DIR}/game/bg_misc.c
+ ${SOURCE_DIR}/game/bg_pmove.c
+ ${SOURCE_DIR}/game/bg_slidemove.c
+ ${SOURCE_DIR}/game/bg_lib.c
+ ${SOURCE_DIR}/cgame/cg_consolecmds.c
+ ${SOURCE_DIR}/cgame/cg_draw.c
+ ${SOURCE_DIR}/cgame/cg_drawtools.c
+ ${SOURCE_DIR}/cgame/cg_effects.c
+ ${SOURCE_DIR}/cgame/cg_ents.c
+ ${SOURCE_DIR}/cgame/cg_event.c
+ ${SOURCE_DIR}/cgame/cg_info.c
+ ${SOURCE_DIR}/cgame/cg_localents.c
+ ${SOURCE_DIR}/cgame/cg_marks.c
+ ${SOURCE_DIR}/cgame/cg_particles.c
+ ${SOURCE_DIR}/cgame/cg_players.c
+ ${SOURCE_DIR}/cgame/cg_playerstate.c
+ ${SOURCE_DIR}/cgame/cg_predict.c
+ ${SOURCE_DIR}/cgame/cg_scoreboard.c
+ ${SOURCE_DIR}/cgame/cg_servercmds.c
+ ${SOURCE_DIR}/cgame/cg_snapshot.c
+ ${SOURCE_DIR}/cgame/cg_view.c
+ ${SOURCE_DIR}/cgame/cg_weapons.c
+)
+
+set(CGAME_BINARY_SOURCES ${SOURCE_DIR}/cgame/cg_syscalls.c)
+set(CGAME_QVM_SOURCES ${SOURCE_DIR}/cgame/cg_syscalls.asm)
+
+set(GAME_SOURCES
+ ${SOURCE_DIR}/game/g_main.c
+ ${SOURCE_DIR}/game/ai_chat.c
+ ${SOURCE_DIR}/game/ai_cmd.c
+ ${SOURCE_DIR}/game/ai_dmnet.c
+ ${SOURCE_DIR}/game/ai_dmq3.c
+ ${SOURCE_DIR}/game/ai_main.c
+ ${SOURCE_DIR}/game/ai_team.c
+ ${SOURCE_DIR}/game/ai_vcmd.c
+ ${SOURCE_DIR}/game/bg_misc.c
+ ${SOURCE_DIR}/game/bg_pmove.c
+ ${SOURCE_DIR}/game/bg_slidemove.c
+ ${SOURCE_DIR}/game/bg_lib.c
+ ${SOURCE_DIR}/game/g_active.c
+ ${SOURCE_DIR}/game/g_arenas.c
+ ${SOURCE_DIR}/game/g_bot.c
+ ${SOURCE_DIR}/game/g_client.c
+ ${SOURCE_DIR}/game/g_cmds.c
+ ${SOURCE_DIR}/game/g_combat.c
+ ${SOURCE_DIR}/game/g_items.c
+ ${SOURCE_DIR}/game/g_mem.c
+ ${SOURCE_DIR}/game/g_misc.c
+ ${SOURCE_DIR}/game/g_missile.c
+ ${SOURCE_DIR}/game/g_mover.c
+ ${SOURCE_DIR}/game/g_session.c
+ ${SOURCE_DIR}/game/g_spawn.c
+ ${SOURCE_DIR}/game/g_svcmds.c
+ ${SOURCE_DIR}/game/g_target.c
+ ${SOURCE_DIR}/game/g_team.c
+ ${SOURCE_DIR}/game/g_trigger.c
+ ${SOURCE_DIR}/game/g_utils.c
+ ${SOURCE_DIR}/game/g_weapon.c
+)
+
+set(GAME_BINARY_SOURCES ${SOURCE_DIR}/game/g_syscalls.c)
+set(GAME_QVM_SOURCES ${SOURCE_DIR}/game/g_syscalls.asm)
+
+set(UI_SOURCES
+ ${SOURCE_DIR}/q3_ui/ui_main.c
+ ${SOURCE_DIR}/game/bg_misc.c
+ ${SOURCE_DIR}/game/bg_lib.c
+ ${SOURCE_DIR}/q3_ui/ui_addbots.c
+ ${SOURCE_DIR}/q3_ui/ui_atoms.c
+ ${SOURCE_DIR}/q3_ui/ui_cdkey.c
+ ${SOURCE_DIR}/q3_ui/ui_cinematics.c
+ ${SOURCE_DIR}/q3_ui/ui_confirm.c
+ ${SOURCE_DIR}/q3_ui/ui_connect.c
+ ${SOURCE_DIR}/q3_ui/ui_controls2.c
+ ${SOURCE_DIR}/q3_ui/ui_credits.c
+ ${SOURCE_DIR}/q3_ui/ui_demo2.c
+ ${SOURCE_DIR}/q3_ui/ui_display.c
+ ${SOURCE_DIR}/q3_ui/ui_gameinfo.c
+ ${SOURCE_DIR}/q3_ui/ui_ingame.c
+ ${SOURCE_DIR}/q3_ui/ui_loadconfig.c
+ ${SOURCE_DIR}/q3_ui/ui_menu.c
+ ${SOURCE_DIR}/q3_ui/ui_mfield.c
+ ${SOURCE_DIR}/q3_ui/ui_mods.c
+ ${SOURCE_DIR}/q3_ui/ui_network.c
+ ${SOURCE_DIR}/q3_ui/ui_options.c
+ ${SOURCE_DIR}/q3_ui/ui_playermodel.c
+ ${SOURCE_DIR}/q3_ui/ui_players.c
+ ${SOURCE_DIR}/q3_ui/ui_playersettings.c
+ ${SOURCE_DIR}/q3_ui/ui_preferences.c
+ ${SOURCE_DIR}/q3_ui/ui_qmenu.c
+ ${SOURCE_DIR}/q3_ui/ui_removebots.c
+ ${SOURCE_DIR}/q3_ui/ui_saveconfig.c
+ ${SOURCE_DIR}/q3_ui/ui_serverinfo.c
+ ${SOURCE_DIR}/q3_ui/ui_servers2.c
+ ${SOURCE_DIR}/q3_ui/ui_setup.c
+ ${SOURCE_DIR}/q3_ui/ui_sound.c
+ ${SOURCE_DIR}/q3_ui/ui_sparena.c
+ ${SOURCE_DIR}/q3_ui/ui_specifyserver.c
+ ${SOURCE_DIR}/q3_ui/ui_splevel.c
+ ${SOURCE_DIR}/q3_ui/ui_sppostgame.c
+ ${SOURCE_DIR}/q3_ui/ui_spskill.c
+ ${SOURCE_DIR}/q3_ui/ui_startserver.c
+ ${SOURCE_DIR}/q3_ui/ui_team.c
+ ${SOURCE_DIR}/q3_ui/ui_teamorders.c
+ ${SOURCE_DIR}/q3_ui/ui_video.c
+)
+
+set(UI_BINARY_SOURCES ${SOURCE_DIR}/ui/ui_syscalls.c)
+set(UI_QVM_SOURCES ${SOURCE_DIR}/ui/ui_syscalls.asm)
+
+set(GAME_MODULE_SHARED_SOURCES
+ ${SOURCE_DIR}/qcommon/q_math.c
+ ${SOURCE_DIR}/qcommon/q_shared.c
+)
+
+set(CGAME_SOURCES_BASEGAME ${CGAME_SOURCES} ${GAME_MODULE_SHARED_SOURCES})
+set(GAME_SOURCES_BASEGAME ${GAME_SOURCES} ${GAME_MODULE_SHARED_SOURCES})
+set(UI_SOURCES_BASEGAME ${UI_SOURCES} ${GAME_MODULE_SHARED_SOURCES})
+
+if(BUILD_GAME_LIBRARIES)
+ if(USE_ARCHLESS_FILENAMES)
+ set(CGAME_MODULE_BINARY ${CGAME_MODULE})
+ set(GAME_MODULE_BINARY ${GAME_MODULE})
+ set(UI_MODULE_BINARY ${UI_MODULE})
+ else()
+ set(CGAME_MODULE_BINARY ${CGAME_MODULE}${ARCH})
+ set(GAME_MODULE_BINARY ${GAME_MODULE}${ARCH})
+ set(UI_MODULE_BINARY ${UI_MODULE}${ARCH})
+ endif()
+
+ set(CGAME_MODULE_BINARY_BASEGAME ${CGAME_MODULE_BINARY}_${BASEGAME})
+ set(GAME_MODULE_BINARY_BASEGAME ${GAME_MODULE_BINARY}_${BASEGAME})
+ set(UI_MODULE_BINARY_BASEGAME ${UI_MODULE_BINARY}_${BASEGAME})
+
+ add_library( ${CGAME_MODULE_BINARY_BASEGAME} SHARED ${CGAME_SOURCES_BASEGAME} ${CGAME_BINARY_SOURCES})
+ target_compile_definitions( ${CGAME_MODULE_BINARY_BASEGAME} PRIVATE CGAME)
+ set_target_properties( ${CGAME_MODULE_BINARY_BASEGAME} PROPERTIES OUTPUT_NAME ${CGAME_MODULE_BINARY})
+ set_output_dirs( ${CGAME_MODULE_BINARY_BASEGAME} SUBDIRECTORY ${BASEGAME})
+
+ add_library( ${GAME_MODULE_BINARY_BASEGAME} SHARED ${GAME_SOURCES_BASEGAME} ${GAME_BINARY_SOURCES})
+ target_compile_definitions( ${GAME_MODULE_BINARY_BASEGAME} PRIVATE QAGAME)
+ set_target_properties( ${GAME_MODULE_BINARY_BASEGAME} PROPERTIES OUTPUT_NAME ${GAME_MODULE_BINARY})
+ set_output_dirs( ${GAME_MODULE_BINARY_BASEGAME} SUBDIRECTORY ${BASEGAME})
+
+ add_library( ${UI_MODULE_BINARY_BASEGAME} SHARED ${UI_SOURCES_BASEGAME} ${UI_BINARY_SOURCES})
+ target_compile_definitions( ${UI_MODULE_BINARY_BASEGAME} PRIVATE UI)
+ set_target_properties( ${UI_MODULE_BINARY_BASEGAME} PROPERTIES OUTPUT_NAME ${UI_MODULE_BINARY})
+ set_output_dirs( ${UI_MODULE_BINARY_BASEGAME} SUBDIRECTORY ${BASEGAME})
+endif()
+
+if(BUILD_GAME_QVMS)
+ set(CGAME_MODULE_QVM_BASEGAME ${CGAME_MODULE}qvm_${BASEGAME})
+ set(GAME_MODULE_QVM_BASEGAME ${GAME_MODULE}qvm_${BASEGAME})
+ set(UI_MODULE_QVM_BASEGAME ${UI_MODULE}qvm_${BASEGAME})
+
+ add_qvm(${CGAME_MODULE_QVM_BASEGAME}
+ DEFINITIONS CGAME
+ OUTPUT_NAME ${CGAME_MODULE}
+ OUTPUT_DIRECTORY ${BASEGAME}/vm
+ SOURCES ${CGAME_SOURCES_BASEGAME} ${CGAME_QVM_SOURCES})
+
+ add_qvm(${GAME_MODULE_QVM_BASEGAME}
+ DEFINITIONS QAGAME
+ OUTPUT_NAME ${GAME_MODULE}
+ OUTPUT_DIRECTORY ${BASEGAME}/vm
+ SOURCES ${GAME_SOURCES_BASEGAME} ${GAME_QVM_SOURCES})
+
+ add_qvm(${UI_MODULE_QVM_BASEGAME}
+ DEFINITIONS UI
+ OUTPUT_NAME ${UI_MODULE}
+ OUTPUT_DIRECTORY ${BASEGAME}/vm
+ SOURCES ${UI_SOURCES_BASEGAME} ${UI_QVM_SOURCES})
+endif()
diff --git a/cmake/client.cmake b/cmake/client.cmake
new file mode 100644
index 00000000..2af6b6fc
--- /dev/null
+++ b/cmake/client.cmake
@@ -0,0 +1,115 @@
+if(NOT BUILD_CLIENT)
+ return()
+endif()
+
+include(utils/add_git_dependency)
+include(utils/arch)
+include(utils/set_output_dirs)
+include(shared_sources)
+
+include(renderer_common)
+
+set(CLIENT_SOURCES
+ ${SOURCE_DIR}/client/cl_cgame.c
+ ${SOURCE_DIR}/client/cl_cin.c
+ ${SOURCE_DIR}/client/cl_console.c
+ ${SOURCE_DIR}/client/cl_input.c
+ ${SOURCE_DIR}/client/cl_keys.c
+ ${SOURCE_DIR}/client/cl_main.c
+ ${SOURCE_DIR}/client/cl_net_chan.c
+ ${SOURCE_DIR}/client/cl_parse.c
+ ${SOURCE_DIR}/client/cl_scrn.c
+ ${SOURCE_DIR}/client/cl_ui.c
+ ${SOURCE_DIR}/client/cl_avi.c
+ ${SOURCE_DIR}/client/libmumblelink.c
+ ${SOURCE_DIR}/client/snd_altivec.c
+ ${SOURCE_DIR}/client/snd_adpcm.c
+ ${SOURCE_DIR}/client/snd_dma.c
+ ${SOURCE_DIR}/client/snd_mem.c
+ ${SOURCE_DIR}/client/snd_mix.c
+ ${SOURCE_DIR}/client/snd_wavelet.c
+ ${SOURCE_DIR}/client/snd_main.c
+ ${SOURCE_DIR}/client/snd_codec.c
+ ${SOURCE_DIR}/client/snd_codec_wav.c
+ ${SOURCE_DIR}/client/snd_codec_ogg.c
+ ${SOURCE_DIR}/client/snd_codec_opus.c
+ ${SOURCE_DIR}/client/qal.c
+ ${SOURCE_DIR}/client/snd_openal.c
+ ${SOURCE_DIR}/sdl/sdl_input.c
+ ${SOURCE_DIR}/sdl/sdl_snd.c
+ ${CLIENT_PLATFORM_SOURCES}
+)
+
+add_git_dependency(${SOURCE_DIR}/client/cl_console.c)
+
+if(USE_ARCHLESS_FILENAMES)
+ set(CLIENT_BINARY ${CLIENT_NAME})
+ list(APPEND CLIENT_DEFINITIONS USE_ARCHLESS_FILENAMES)
+else()
+ set(CLIENT_BINARY ${CLIENT_NAME}.${ARCH})
+endif()
+
+list(APPEND CLIENT_DEFINITIONS BOTLIB)
+
+if(BUILD_STANDALONE)
+ list(APPEND CLIENT_DEFINITIONS STANDALONE)
+endif()
+
+if(USE_RENDERER_DLOPEN)
+ list(APPEND CLIENT_DEFINITIONS USE_RENDERER_DLOPEN)
+endif()
+
+if(USE_HTTP)
+ list(APPEND CLIENT_DEFINITIONS USE_HTTP)
+endif()
+
+if(USE_VOIP)
+ list(APPEND CLIENT_DEFINITIONS USE_VOIP)
+endif()
+
+if(USE_MUMBLE)
+ list(APPEND CLIENT_DEFINITIONS USE_MUMBLE)
+ list(APPEND CLIENT_LIBRARY_SOURCES ${SOURCE_DIR}/client/libmumblelink.c)
+endif()
+
+list(APPEND CLIENT_BINARY_SOURCES
+ ${SERVER_SOURCES}
+ ${CLIENT_SOURCES}
+ ${COMMON_SOURCES}
+ ${BOTLIB_SOURCES}
+ ${SYSTEM_SOURCES}
+ ${ASM_SOURCES}
+ ${CLIENT_LIBRARY_SOURCES})
+
+add_executable(${CLIENT_BINARY} ${CLIENT_EXECUTABLE_OPTIONS} ${CLIENT_BINARY_SOURCES})
+
+target_include_directories( ${CLIENT_BINARY} PRIVATE ${CLIENT_INCLUDE_DIRS})
+target_compile_definitions( ${CLIENT_BINARY} PRIVATE ${CLIENT_DEFINITIONS})
+target_compile_options( ${CLIENT_BINARY} PRIVATE ${CLIENT_COMPILE_OPTIONS})
+target_link_libraries( ${CLIENT_BINARY} PRIVATE ${COMMON_LIBRARIES} ${CLIENT_LIBRARIES})
+target_link_options( ${CLIENT_BINARY} PRIVATE ${CLIENT_LINK_OPTIONS})
+
+set_output_dirs(${CLIENT_BINARY})
+
+if(NOT USE_RENDERER_DLOPEN)
+ target_sources(${CLIENT_BINARY} PRIVATE
+ # These are never simultaneously populated
+ ${RENDERER_GL1_BINARY_SOURCES}
+ ${RENDERER_GL2_BINARY_SOURCES})
+
+ target_include_directories( ${CLIENT_BINARY} PRIVATE ${RENDERER_INCLUDE_DIRS})
+ target_compile_definitions( ${CLIENT_BINARY} PRIVATE ${RENDERER_DEFINITIONS})
+ target_compile_options( ${CLIENT_BINARY} PRIVATE ${RENDERER_COMPILE_OPTIONS})
+ target_link_libraries( ${CLIENT_BINARY} PRIVATE ${RENDERER_LIBRARIES})
+endif()
+
+foreach(LIBRARY IN LISTS CLIENT_DEPLOY_LIBRARIES)
+ add_custom_command(TARGET ${CLIENT_BINARY} POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${LIBRARY}
+ $)
+endforeach()
+
+if(POST_CLIENT_CONFIGURE_FUNCTION)
+ cmake_language(CALL ${POST_CLIENT_CONFIGURE_FUNCTION})
+endif()
diff --git a/cmake/compilers/all.cmake b/cmake/compilers/all.cmake
new file mode 100644
index 00000000..0f3db942
--- /dev/null
+++ b/cmake/compilers/all.cmake
@@ -0,0 +1,5 @@
+include(compilers/appleclang)
+include(compilers/clang)
+include(compilers/gcc)
+include(compilers/gnu)
+include(compilers/msvc)
diff --git a/cmake/compilers/appleclang.cmake b/cmake/compilers/appleclang.cmake
new file mode 100644
index 00000000..d865a79b
--- /dev/null
+++ b/cmake/compilers/appleclang.cmake
@@ -0,0 +1,5 @@
+# Apple Clang compiler specific settings
+
+if(NOT CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
+ return()
+endif()
diff --git a/cmake/compilers/clang.cmake b/cmake/compilers/clang.cmake
new file mode 100644
index 00000000..19650b62
--- /dev/null
+++ b/cmake/compilers/clang.cmake
@@ -0,0 +1,5 @@
+# Clang compiler specific settings
+
+if(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang")
+ return()
+endif()
diff --git a/cmake/compilers/gcc.cmake b/cmake/compilers/gcc.cmake
new file mode 100644
index 00000000..b3d7282c
--- /dev/null
+++ b/cmake/compilers/gcc.cmake
@@ -0,0 +1,5 @@
+# GCC compiler specific settings
+
+if(NOT CMAKE_C_COMPILER_ID STREQUAL "GNU")
+ return()
+endif()
diff --git a/cmake/compilers/gnu.cmake b/cmake/compilers/gnu.cmake
new file mode 100644
index 00000000..cb2ef0ad
--- /dev/null
+++ b/cmake/compilers/gnu.cmake
@@ -0,0 +1,18 @@
+# GNU style (GCC/Clang) compiler specific settings
+
+if(NOT CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_C_COMPILER_ID MATCHES "^(Apple)?Clang$")
+ return()
+endif()
+
+set(ASM_SOURCES
+ ${SOURCE_DIR}/asm/snapvector.c
+ ${SOURCE_DIR}/asm/ftola.c
+)
+
+add_compile_options(-Wall -Wimplicit
+ -Wstrict-prototypes -Wformat=2 -Wformat-security
+ -Wstrict-aliasing=2 -Wmissing-format-attribute
+ -Wdisabled-optimization -Werror-implicit-function-declaration)
+
+add_compile_options(-Wno-strict-aliasing
+ -Wno-format-zero-length -Wno-format-nonliteral)
diff --git a/cmake/compilers/msvc.cmake b/cmake/compilers/msvc.cmake
new file mode 100644
index 00000000..55935403
--- /dev/null
+++ b/cmake/compilers/msvc.cmake
@@ -0,0 +1,37 @@
+# MSVC compiler specific settings
+
+if(NOT CMAKE_C_COMPILER_ID STREQUAL "MSVC")
+ return()
+endif()
+
+include(utils/arch)
+
+enable_language(ASM_MASM)
+
+set(ASM_SOURCES
+ ${SOURCE_DIR}/asm/snapvector.asm
+ ${SOURCE_DIR}/asm/ftola.asm
+)
+
+if(ARCH MATCHES "x86_64")
+ list(APPEND ASM_SOURCES ${SOURCE_DIR}/asm/vm_x86_64.asm)
+ set_source_files_properties(
+ ${ASM_SOURCES}
+ PROPERTIES COMPILE_DEFINITIONS "idx64")
+endif()
+
+# Baseline warnings
+add_compile_options("$<$:/W4>")
+
+# C4267: 'var' : conversion from 'size_t' to 'type', possible loss of data
+# There are way too many of these to realistically deal with them
+add_compile_options("$<$:/wd4267>")
+
+# MSVC doesn't understand __inline__, which libjpeg uses
+add_compile_definitions(__inline__=inline)
+
+# It's unlikely that we'll move to the _s variants, so stop the warning
+add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
+
+# The sockets platform abstraction layer necessarily uses deprecated APIs
+add_compile_definitions(_WINSOCK_DEPRECATED_NO_WARNINGS)
diff --git a/cmake/identity.cmake b/cmake/identity.cmake
new file mode 100644
index 00000000..796d7d86
--- /dev/null
+++ b/cmake/identity.cmake
@@ -0,0 +1,20 @@
+set(PROJECT_NAME ioq3)
+set(PROJECT_VERSION 1.36)
+
+set(SERVER_NAME ioq3ded)
+set(CLIENT_NAME ioquake3)
+
+set(BASEGAME baseq3)
+
+set(CGAME_MODULE cgame)
+set(GAME_MODULE qagame)
+set(UI_MODULE ui)
+
+set(WINDOWS_ICON_PATH ${CMAKE_SOURCE_DIR}/misc/quake3.ico)
+
+set(MACOS_ICON_PATH ${CMAKE_SOURCE_DIR}/misc/quake3_flat.icns)
+set(MACOS_BUNDLE_ID org.ioquake.${CLIENT_NAME})
+
+set(COPYRIGHT "QUAKE III ARENA Copyright © 1999-2000 id Software, Inc. All rights reserved.")
+
+set(PROTOCOL_HANDLER_SCHEME quake3)
diff --git a/cmake/libraries/all.cmake b/cmake/libraries/all.cmake
new file mode 100644
index 00000000..112c3489
--- /dev/null
+++ b/cmake/libraries/all.cmake
@@ -0,0 +1,9 @@
+include(libraries/curl)
+include(libraries/freetype)
+include(libraries/jpeg)
+include(libraries/ogg)
+include(libraries/opus)
+include(libraries/openal)
+include(libraries/sdl)
+include(libraries/vorbis)
+include(libraries/zlib)
diff --git a/cmake/libraries/curl.cmake b/cmake/libraries/curl.cmake
new file mode 100644
index 00000000..c0ca61eb
--- /dev/null
+++ b/cmake/libraries/curl.cmake
@@ -0,0 +1,15 @@
+if(NOT USE_HTTP OR WIN32)
+ return()
+endif()
+
+set(INTERNAL_CURL_DIR ${SOURCE_DIR}/thirdparty/curl-8.15.0)
+
+find_package(CURL QUIET)
+
+if(NOT CURL_FOUND)
+ set(CURL_DEFINITIONS USE_INTERNAL_CURL_HEADERS)
+ set(CURL_INCLUDE_DIR ${INTERNAL_CURL_DIR}/include)
+endif()
+
+list(APPEND CLIENT_DEFINITIONS ${CURL_DEFINITIONS})
+list(APPEND CLIENT_INCLUDE_DIRS ${CURL_INCLUDE_DIR})
diff --git a/cmake/libraries/freetype.cmake b/cmake/libraries/freetype.cmake
new file mode 100644
index 00000000..45bac275
--- /dev/null
+++ b/cmake/libraries/freetype.cmake
@@ -0,0 +1,8 @@
+if(NOT USE_FREETYPE)
+ return()
+endif()
+
+find_package(Freetype REQUIRED)
+
+list(APPEND RENDERER_INCLUDE_DIRS ${FREETYPE_INCLUDE_DIRS})
+list(APPEND RENDERER_LIBRARIES ${FREETYPE_LIBRARIES})
diff --git a/cmake/libraries/jpeg.cmake b/cmake/libraries/jpeg.cmake
new file mode 100644
index 00000000..ccde8a8d
--- /dev/null
+++ b/cmake/libraries/jpeg.cmake
@@ -0,0 +1,18 @@
+include(utils/disable_warnings)
+include(utils/find_include_dirs)
+
+set(INTERNAL_JPEG_DIR ${SOURCE_DIR}/thirdparty/jpeg-9f)
+
+if(USE_INTERNAL_JPEG)
+ file(GLOB_RECURSE JPEG_SOURCES ${INTERNAL_JPEG_DIR}/j*.c)
+ disable_warnings(${JPEG_SOURCES})
+ find_include_dirs(JPEG_INCLUDE_DIRS ${JPEG_SOURCES})
+ set(JPEG_DEFINITIONS USE_INTERNAL_JPEG)
+ list(APPEND RENDERER_LIBRARY_SOURCES ${JPEG_SOURCES})
+else()
+ find_package(JPEG REQUIRED)
+endif()
+
+list(APPEND RENDERER_LIBRARIES ${JPEG_LIBRARIES})
+list(APPEND RENDERER_INCLUDE_DIRS ${JPEG_INCLUDE_DIRS})
+list(APPEND RENDERER_DEFINITIONS ${JPEG_DEFINTIONS})
diff --git a/cmake/libraries/ogg.cmake b/cmake/libraries/ogg.cmake
new file mode 100644
index 00000000..c6112854
--- /dev/null
+++ b/cmake/libraries/ogg.cmake
@@ -0,0 +1,21 @@
+if(NOT USE_CODEC_VORBIS)
+ return()
+endif()
+
+include(utils/disable_warnings)
+
+set(INTERNAL_OGG_DIR ${SOURCE_DIR}/thirdparty/libogg-1.3.6)
+
+if(USE_INTERNAL_OGG)
+ file(GLOB_RECURSE OGG_SOURCES ${INTERNAL_OGG_DIR}/*.c)
+ disable_warnings(${OGG_SOURCES})
+ set(OGG_INCLUDE_DIRS ${INTERNAL_OGG_DIR}/include)
+ list(APPEND CLIENT_LIBRARY_SOURCES ${OGG_SOURCES})
+else()
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(OGG REQUIRED ogg)
+endif()
+
+list(APPEND CLIENT_LIBRARIES ${OGG_LIBRARIES})
+list(APPEND CLIENT_INCLUDE_DIRS ${OGG_INCLUDE_DIRS})
+list(APPEND CLIENT_DEFINITIONS ${OGG_DEFINITIONS})
diff --git a/cmake/libraries/openal.cmake b/cmake/libraries/openal.cmake
new file mode 100644
index 00000000..95b2d345
--- /dev/null
+++ b/cmake/libraries/openal.cmake
@@ -0,0 +1,23 @@
+if(NOT USE_OPENAL)
+ return()
+endif()
+
+set(INTERNAL_OPENAL_DIR ${SOURCE_DIR}/thirdparty/openal-soft-1.24.3)
+
+find_package(OpenAL QUIET)
+
+if(NOT OpenAL_FOUND)
+ set(OPENAL_DEFINITIONS USE_INTERNAL_OPENAL_HEADERS)
+ set(OPENAL_INCLUDE_DIR ${INTERNAL_OPENAL_DIR}/include)
+ set(OPENAL_LIBRARY openal)
+endif()
+
+list(APPEND CLIENT_DEFINITIONS ${OPENAL_DEFINITIONS} USE_OPENAL)
+list(APPEND CLIENT_INCLUDE_DIRS ${OPENAL_INCLUDE_DIR})
+
+if(USE_OPENAL_DLOPEN)
+ list(APPEND CLIENT_DEFINITIONS USE_OPENAL_DLOPEN)
+else()
+ find_package(Threads REQUIRED)
+ list(APPEND CLIENT_LIBRARIES Threads::Threads ${OPENAL_LIBRARY})
+endif()
diff --git a/cmake/libraries/opus.cmake b/cmake/libraries/opus.cmake
new file mode 100644
index 00000000..01dc1ef2
--- /dev/null
+++ b/cmake/libraries/opus.cmake
@@ -0,0 +1,27 @@
+if(NOT USE_CODEC_OPUS)
+ return()
+endif()
+
+include(utils/disable_warnings)
+include(utils/find_include_dirs)
+
+set(INTERNAL_OPUS_DIR ${SOURCE_DIR}/thirdparty/opus-1.5.2)
+set(INTERNAL_OPUSFILE_DIR ${SOURCE_DIR}/thirdparty/opusfile-0.12)
+
+if(USE_INTERNAL_OPUS)
+ file(GLOB_RECURSE OPUS_SOURCES ${INTERNAL_OPUS_DIR}/*.c)
+ file(GLOB_RECURSE OPUSFILE_SOURCES ${INTERNAL_OPUSFILE_DIR}/*.c)
+ disable_warnings(${OPUS_SOURCES} ${OPUSFILE_SOURCES})
+ find_include_dirs(OPUS_INCLUDE_DIRS ${OPUS_SOURCES})
+ find_include_dirs(OPUSFILE_INCLUDE_DIRS ${OPUSFILE_SOURCES})
+ set(OPUS_INCLUDE_DIRS ${OPUS_INCLUDE_DIRS} ${OPUSFILE_INCLUDE_DIRS} ${INTERNAL_OPUSFILE_DIR}/include)
+ set(OPUS_DEFINITIONS USE_CODEC_OPUS OPUS_BUILD HAVE_LRINTF FLOATING_POINT FLOAT_APPROX USE_ALLOCA)
+ list(APPEND CLIENT_LIBRARY_SOURCES ${OPUS_SOURCES} ${OPUSFILE_SOURCES})
+else()
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(OPUS REQUIRED opus)
+endif()
+
+list(APPEND CLIENT_LIBRARIES ${OPUS_LIBRARIES})
+list(APPEND CLIENT_INCLUDE_DIRS ${OPUS_INCLUDE_DIRS})
+list(APPEND CLIENT_DEFINITIONS ${OPUS_DEFINITIONS})
diff --git a/cmake/libraries/sdl.cmake b/cmake/libraries/sdl.cmake
new file mode 100644
index 00000000..b1e81ae5
--- /dev/null
+++ b/cmake/libraries/sdl.cmake
@@ -0,0 +1,51 @@
+set(INTERNAL_SDL_DIR ${SOURCE_DIR}/thirdparty/SDL2-2.32.8)
+
+include(utils/arch)
+
+if(NOT WIN32 AND NOT APPLE)
+ set(SYSTEM_SDL_REQUIRED REQUIRED)
+endif()
+
+find_package(SDL2 QUIET ${SYSTEM_SDL_REQUIRED})
+
+if(NOT SDL2_FOUND)
+ set(SDL2_INCLUDE_DIRS ${INTERNAL_SDL_DIR}/include)
+
+ # On Windows and macOS we have internal SDL binaries we can use
+ if(WIN32)
+ if(ARCH STREQUAL "x86_64")
+ set(LIB_DIR ${SOURCE_DIR}/thirdparty/libs/win64)
+ elseif(ARCH STREQUAL "x86")
+ set(LIB_DIR ${SOURCE_DIR}/thirdparty/libs/win32)
+ else()
+ message(FATAL_ERROR "Unknown ARCH")
+ endif()
+
+ if(MINGW)
+ set(SDL2_LIBRARIES
+ ${LIB_DIR}/libSDL2main.a
+ ${LIB_DIR}/libSDL2.dll.a)
+ elseif(MSVC)
+ set(SDL2_LIBRARIES
+ ${LIB_DIR}/SDL2main.lib
+ ${LIB_DIR}/SDL2.lib)
+ endif()
+
+ list(APPEND CLIENT_DEPLOY_LIBRARIES ${LIB_DIR}/SDL2.dll)
+ elseif(APPLE)
+ set(SDL2_LIBRARIES
+ ${SOURCE_DIR}/thirdparty/libs/macos/libSDL2main.a
+ ${SOURCE_DIR}/thirdparty/libs/macos/libSDL2-2.0.0.dylib)
+ list(APPEND CLIENT_DEPLOY_LIBRARIES
+ ${SOURCE_DIR}/thirdparty/libs/macos/libSDL2-2.0.0.dylib)
+ else()
+ message(FATAL_ERROR "SDL2 not found and no internal binaries available")
+ endif()
+endif()
+
+list(APPEND CLIENT_LIBRARIES ${SDL2_LIBRARIES})
+list(APPEND CLIENT_INCLUDE_DIRS ${SDL2_INCLUDE_DIRS})
+list(APPEND CLIENT_COMPILE_OPTIONS ${SDL2_CFLAGS_OTHER})
+list(APPEND RENDERER_LIBRARIES ${SDL2_LIBRARIES})
+list(APPEND RENDERER_INCLUDE_DIRS ${SDL2_INCLUDE_DIRS})
+list(APPEND RENDERER_COMPILE_OPTIONS ${SDL2_CFLAGS_OTHER})
diff --git a/cmake/libraries/vorbis.cmake b/cmake/libraries/vorbis.cmake
new file mode 100644
index 00000000..d5424162
--- /dev/null
+++ b/cmake/libraries/vorbis.cmake
@@ -0,0 +1,22 @@
+if(NOT USE_CODEC_VORBIS)
+ return()
+endif()
+
+include(utils/disable_warnings)
+
+set(INTERNAL_VORBIS_DIR ${SOURCE_DIR}/thirdparty/libvorbis-1.3.7)
+
+if(USE_INTERNAL_VORBIS)
+ file(GLOB_RECURSE VORBIS_SOURCES ${INTERNAL_VORBIS_DIR}/*.c)
+ disable_warnings(${VORBIS_SOURCES})
+ set(VORBIS_INCLUDE_DIRS ${INTERNAL_VORBIS_DIR}/include ${INTERNAL_VORBIS_DIR}/lib)
+ set(VORBIS_DEFINITIONS USE_CODEC_VORBIS)
+ list(APPEND CLIENT_LIBRARY_SOURCES ${VORBIS_SOURCES})
+else()
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(VORBIS REQUIRED vorbis)
+endif()
+
+list(APPEND CLIENT_LIBRARIES ${VORBIS_LIBRARIES})
+list(APPEND CLIENT_INCLUDE_DIRS ${VORBIS_INCLUDE_DIRS})
+list(APPEND CLIENT_DEFINITIONS ${VORBIS_DEFINITIONS})
diff --git a/cmake/libraries/zlib.cmake b/cmake/libraries/zlib.cmake
new file mode 100644
index 00000000..44918e6c
--- /dev/null
+++ b/cmake/libraries/zlib.cmake
@@ -0,0 +1,22 @@
+include(utils/disable_warnings)
+include(utils/find_include_dirs)
+
+set(INTERNAL_ZLIB_DIR ${SOURCE_DIR}/thirdparty/zlib-1.3.1)
+
+if(USE_INTERNAL_ZLIB)
+ file(GLOB_RECURSE ZLIB_SOURCES ${INTERNAL_ZLIB_DIR}/*.c)
+ disable_warnings(ZLIB_SOURCES)
+ find_include_dirs(ZLIB_INCLUDE_DIRS ${ZLIB_SOURCES})
+ set(ZLIB_DEFINITIONS NO_GZIP)
+ list(APPEND SERVER_LIBRARY_SOURCES ${ZLIB_SOURCES})
+ list(APPEND CLIENT_LIBRARY_SOURCES ${ZLIB_SOURCES})
+else()
+ find_package(ZLIB REQUIRED)
+endif()
+
+list(APPEND SERVER_LIBRARIES ${ZLIB_LIBRARIES})
+list(APPEND SERVER_INCLUDE_DIRS ${ZLIB_INCLUDE_DIRS})
+list(APPEND SERVER_DEFINITIONS ${ZLIB_DEFINITIONS})
+list(APPEND CLIENT_LIBRARIES ${ZLIB_LIBRARIES})
+list(APPEND CLIENT_INCLUDE_DIRS ${ZLIB_INCLUDE_DIRS})
+list(APPEND CLIENT_DEFINITIONS ${ZLIB_DEFINITIONS})
diff --git a/cmake/missionpack.cmake b/cmake/missionpack.cmake
new file mode 100644
index 00000000..34f94577
--- /dev/null
+++ b/cmake/missionpack.cmake
@@ -0,0 +1,72 @@
+if(NOT BUILD_GAME_LIBRARIES AND NOT BUILD_GAME_QVMS)
+ return()
+endif()
+
+include(utils/qvm_tools)
+include(utils/set_output_dirs)
+
+set(MPCGAME_SOURCES
+ ${SOURCE_DIR}/cgame/cg_newdraw.c
+ ${SOURCE_DIR}/ui/ui_shared.c
+)
+
+set(MPUI_SOURCES
+ ${SOURCE_DIR}/ui/ui_main.c
+ ${SOURCE_DIR}/ui/ui_atoms.c
+ ${SOURCE_DIR}/ui/ui_gameinfo.c
+ ${SOURCE_DIR}/ui/ui_players.c
+ ${SOURCE_DIR}/ui/ui_shared.c
+ ${SOURCE_DIR}/game/bg_misc.c
+ ${SOURCE_DIR}/game/bg_lib.c
+)
+
+set(MISSIONPACK "missionpack")
+
+set(CGAME_SOURCES_MISSIONPACK ${CGAME_SOURCES} ${MPCGAME_SOURCES} ${GAME_MODULE_SHARED_SOURCES})
+set(GAME_SOURCES_MISSIONPACK ${GAME_SOURCES} ${GAME_MODULE_SHARED_SOURCES})
+set(UI_SOURCES_MISSIONPACK ${MPUI_SOURCES} ${GAME_MODULE_SHARED_SOURCES})
+
+if(BUILD_GAME_LIBRARIES)
+ set(CGAME_MODULE_BINARY_MISSIONPACK ${CGAME_MODULE_BINARY}_${MISSIONPACK})
+ set(GAME_MODULE_BINARY_MISSIONPACK ${GAME_MODULE_BINARY}_${MISSIONPACK})
+ set(UI_MODULE_BINARY_MISSIONPACK ${UI_MODULE_BINARY}_${MISSIONPACK})
+
+ add_library( ${CGAME_MODULE_BINARY_MISSIONPACK} SHARED ${CGAME_SOURCES_MISSIONPACK} ${CGAME_BINARY_SOURCES})
+ target_compile_definitions( ${CGAME_MODULE_BINARY_MISSIONPACK} PRIVATE CGAME MISSIONPACK)
+ set_target_properties( ${CGAME_MODULE_BINARY_MISSIONPACK} PROPERTIES OUTPUT_NAME ${CGAME_MODULE_BINARY})
+ set_output_dirs( ${CGAME_MODULE_BINARY_MISSIONPACK} SUBDIRECTORY ${MISSIONPACK})
+
+ add_library( ${GAME_MODULE_BINARY_MISSIONPACK} SHARED ${GAME_SOURCES_MISSIONPACK} ${GAME_BINARY_SOURCES})
+ target_compile_definitions( ${GAME_MODULE_BINARY_MISSIONPACK} PRIVATE QAGAME MISSIONPACK)
+ set_target_properties( ${GAME_MODULE_BINARY_MISSIONPACK} PROPERTIES OUTPUT_NAME ${GAME_MODULE_BINARY})
+ set_output_dirs( ${GAME_MODULE_BINARY_MISSIONPACK} SUBDIRECTORY ${MISSIONPACK})
+
+ add_library( ${UI_MODULE_BINARY_MISSIONPACK} SHARED ${UI_SOURCES_MISSIONPACK} ${UI_BINARY_SOURCES})
+ target_compile_definitions( ${UI_MODULE_BINARY_MISSIONPACK} PRIVATE UI MISSIONPACK)
+ set_target_properties( ${UI_MODULE_BINARY_MISSIONPACK} PROPERTIES OUTPUT_NAME ${UI_MODULE_BINARY})
+ set_output_dirs( ${UI_MODULE_BINARY_MISSIONPACK} SUBDIRECTORY ${MISSIONPACK})
+endif()
+
+if(BUILD_GAME_QVMS)
+ set(CGAME_MODULE_QVM_MISSIONPACK ${CGAME_MODULE}qvm_${MISSIONPACK})
+ set(GAME_MODULE_QVM_MISSIONPACK ${GAME_MODULE}qvm_${MISSIONPACK})
+ set(UI_MODULE_QVM_MISSIONPACK ${UI_MODULE}qvm_${MISSIONPACK})
+
+ add_qvm(${CGAME_MODULE_QVM_MISSIONPACK}
+ DEFINITIONS CGAME MISSIONPACK
+ OUTPUT_NAME ${CGAME_MODULE}
+ OUTPUT_DIRECTORY ${MISSIONPACK}/vm
+ SOURCES ${CGAME_SOURCES_MISSIONPACK} ${CGAME_QVM_SOURCES})
+
+ add_qvm(${GAME_MODULE_QVM_MISSIONPACK}
+ DEFINITIONS QAGAME MISSIONPACK
+ OUTPUT_NAME ${GAME_MODULE}
+ OUTPUT_DIRECTORY ${MISSIONPACK}/vm
+ SOURCES ${GAME_SOURCES_MISSIONPACK} ${GAME_QVM_SOURCES})
+
+ add_qvm(${UI_MODULE_QVM_MISSIONPACK}
+ DEFINITIONS UI MISSIONPACK
+ OUTPUT_NAME ${UI_MODULE}
+ OUTPUT_DIRECTORY ${MISSIONPACK}/vm
+ SOURCES ${UI_SOURCES_MISSIONPACK} ${UI_QVM_SOURCES})
+endif()
diff --git a/cmake/platforms/all.cmake b/cmake/platforms/all.cmake
new file mode 100644
index 00000000..7d8ad9a9
--- /dev/null
+++ b/cmake/platforms/all.cmake
@@ -0,0 +1,4 @@
+include(platforms/emscripten)
+include(platforms/macos)
+include(platforms/unix)
+include(platforms/windows)
diff --git a/cmake/platforms/emscripten.cmake b/cmake/platforms/emscripten.cmake
new file mode 100644
index 00000000..03ba18f6
--- /dev/null
+++ b/cmake/platforms/emscripten.cmake
@@ -0,0 +1,50 @@
+# Emscripten specific settings
+
+if(NOT EMSCRIPTEN)
+ return()
+endif()
+
+set(CMAKE_EXECUTABLE_SUFFIX ".js")
+set(CMAKE_SHARED_LIBRARY_SUFFIX ".wasm")
+
+# Disable options that don't make sense for emscripten
+set(BUILD_SERVER OFF CACHE INTERNAL "")
+set(BUILD_RENDERER_GL1 OFF CACHE INTERNAL "")
+set(USE_RENDERER_DLOPEN OFF CACHE INTERNAL "")
+set(USE_OPENAL_DLOPEN OFF CACHE INTERNAL "")
+set(BUILD_GAME_LIBRARIES OFF CACHE INTERNAL "")
+set(USE_HTTP OFF CACHE INTERNAL "")
+
+list(APPEND CLIENT_COMPILE_OPTIONS -sUSE_SDL=2)
+
+list(APPEND CLIENT_LINK_OPTIONS
+ -sTOTAL_MEMORY=256MB
+ -sSTACK_SIZE=5MB
+ -sMIN_WEBGL_VERSION=1
+ -sMAX_WEBGL_VERSION=2
+ -sEXPORTED_RUNTIME_METHODS=FS,addRunDependency,removeRunDependency
+ -sEXIT_RUNTIME=1
+ -sEXPORT_ES6
+ -sEXPORT_NAME=${CLIENT_NAME}
+)
+
+option(EMSCRIPTEN_PRELOAD_FILE "Preload game files into .data file" OFF)
+
+if(EMSCRIPTEN_PRELOAD_FILE)
+ if(NOT EXISTS "${CMAKE_SOURCE_DIR}/${BASEGAME}")
+ message(FATAL_ERROR "No files in '${BASEGAME}' directory for emscripten to preload.")
+ endif()
+ list(APPEND CLIENT_LINK_OPTIONS "--preload-file ${BASEGAME}")
+endif()
+
+set(POST_CLIENT_CONFIGURE_FUNCTION deploy_shell_files)
+
+function(deploy_shell_files)
+ configure_file(${SOURCE_DIR}/web/client.html.in
+ ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${CLIENT_NAME}.html @ONLY)
+
+ if(NOT EMSCRIPTEN_PRELOAD_FILE)
+ configure_file(${SOURCE_DIR}/web/client-config.json
+ ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${CLIENT_NAME}-config.json COPYONLY)
+ endif()
+endfunction()
diff --git a/cmake/platforms/macos.cmake b/cmake/platforms/macos.cmake
new file mode 100644
index 00000000..5e6a998d
--- /dev/null
+++ b/cmake/platforms/macos.cmake
@@ -0,0 +1,80 @@
+# macOS specific settings
+
+if(NOT APPLE)
+ return()
+endif()
+
+# Including the arch in the filename doesn't really make sense
+# on macOS where we're building Universal Binaries
+set(USE_ARCHLESS_FILENAMES ON CACHE INTERNAL "")
+
+option(BUILD_MACOS_APP "Deploy as a macOS .app" ON)
+
+enable_language(OBJC)
+
+list(APPEND SYSTEM_PLATFORM_SOURCES ${SOURCE_DIR}/sys/sys_osx.m)
+
+list(APPEND COMMON_LIBRARIES "-framework Cocoa")
+list(APPEND CLIENT_LIBRARIES "-framework IOKit")
+list(APPEND RENDERER_LIBRARIES "-framework OpenGL")
+
+set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0)
+set(CMAKE_OSX_ARCHITECTURES arm64;x86_64)
+
+if(BUILD_MACOS_APP)
+ set(CLIENT_EXECUTABLE_OPTIONS MACOSX_BUNDLE)
+ set(POST_CLIENT_CONFIGURE_FUNCTION finish_macos_app)
+endif()
+
+function(finish_macos_app)
+ get_filename_component(MACOS_ICON_FILE ${MACOS_ICON_PATH} NAME)
+
+ set(MACOS_APP_BUNDLE_NAME ${CLIENT_NAME})
+ set(MACOS_APP_EXECUTABLE_NAME ${CLIENT_BINARY})
+ set(MACOS_APP_GUI_IDENTIFIER ${MACOS_BUNDLE_ID})
+ set(MACOS_APP_ICON_FILE ${MACOS_ICON_FILE})
+ set(MACOS_APP_SHORT_VERSION_STRING ${PRODUCT_VERSION})
+ set(MACOS_APP_BUNDLE_VERSION ${PRODUCT_VERSION})
+ set(MACOS_APP_DEPLOYMENT_TARGET ${CMAKE_OSX_DEPLOYMENT_TARGET})
+ set(MACOS_APP_COPYRIGHT ${COPYRIGHT})
+
+ if(PROTOCOL_HANDLER_SCHEME)
+ set(MACOS_APP_PLIST_URL_TYPES
+ "CFBundleURLTypes
+
+
+ CFBundleURLName
+ ${MACOS_APP_BUNDLE_NAME}
+ CFBundleURLSchemes
+
+ ${PROTOCOL_HANDLER_SCHEME}
+
+
+ ")
+ else()
+ set(MACOS_APP_PLIST_URL_TYPES "")
+ endif()
+
+ configure_file(${CMAKE_SOURCE_DIR}/cmake/Info.plist.in
+ ${CMAKE_BINARY_DIR}/Info.plist @ONLY)
+
+ set_target_properties(${CLIENT_BINARY} PROPERTIES
+ MACOSX_BUNDLE_INFO_PLIST ${CMAKE_BINARY_DIR}/Info.plist)
+
+ set(RESOURCES_DIR $/../Resources)
+ add_custom_command(TARGET ${CLIENT_BINARY} POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${RESOURCES_DIR}
+ COMMAND ${CMAKE_COMMAND} -E copy ${MACOS_ICON_PATH} ${RESOURCES_DIR})
+
+ if(USE_RENDERER_DLOPEN)
+ set(MACOS_APP_BINARY_DIR ${CLIENT_BINARY}.app/Contents/MacOS)
+
+ if(BUILD_RENDERER_GL1)
+ set_output_dirs(${RENDERER_GL1_BINARY} SUBDIRECTORY ${MACOS_APP_BINARY_DIR})
+ endif()
+
+ if(BUILD_RENDERER_GL2)
+ set_output_dirs(${RENDERER_GL2_BINARY} SUBDIRECTORY ${MACOS_APP_BINARY_DIR})
+ endif()
+ endif()
+endfunction()
diff --git a/cmake/platforms/unix.cmake b/cmake/platforms/unix.cmake
new file mode 100644
index 00000000..97c7aeb7
--- /dev/null
+++ b/cmake/platforms/unix.cmake
@@ -0,0 +1,21 @@
+# Unix specific settings (this includes macOS and emscripten)
+
+if(NOT UNIX)
+ return()
+endif()
+
+list(APPEND SYSTEM_PLATFORM_SOURCES ${SOURCE_DIR}/sys/sys_unix.c)
+
+if(EMSCRIPTEN)
+ list(APPEND SYSTEM_PLATFORM_SOURCES ${SOURCE_DIR}/sys/con_passive.c)
+else()
+ list(APPEND SYSTEM_PLATFORM_SOURCES ${SOURCE_DIR}/sys/con_tty.c)
+endif()
+
+if(USE_HTTP)
+ list(APPEND CLIENT_PLATFORM_SOURCES ${SOURCE_DIR}/client/cl_http_curl.c)
+endif()
+
+list(APPEND COMMON_LIBRARIES dl m)
+
+list(APPEND CLIENT_DEFINITIONS USE_ICON)
diff --git a/cmake/platforms/windows.cmake b/cmake/platforms/windows.cmake
new file mode 100644
index 00000000..15de089d
--- /dev/null
+++ b/cmake/platforms/windows.cmake
@@ -0,0 +1,35 @@
+# Windows specific settings
+
+if(NOT WIN32)
+ return()
+endif()
+
+list(APPEND SYSTEM_PLATFORM_SOURCES
+ ${SOURCE_DIR}/sys/sys_win32.c
+ ${SOURCE_DIR}/sys/con_passive.c
+ ${SOURCE_DIR}/sys/win_resource.rc
+)
+
+if(USE_HTTP)
+ list(APPEND CLIENT_PLATFORM_SOURCES ${SOURCE_DIR}/client/cl_http_windows.c)
+ list(APPEND CLIENT_LIBRARIES wininet)
+endif()
+
+list(APPEND COMMON_LIBRARIES ws2_32 winmm psapi)
+
+if(MINGW)
+ list(APPEND COMMON_LIBRARIES mingw32)
+endif()
+
+list(APPEND CLIENT_DEFINITIONS USE_ICON)
+
+set_source_files_properties(${SOURCE_DIR}/sys/win_resource.rc
+ PROPERTIES COMPILE_DEFINITIONS WINDOWS_ICON_PATH=${WINDOWS_ICON_PATH})
+
+if(MSVC)
+ # We have our own manifest, disable auto creation
+ list(APPEND SERVER_LINK_OPTIONS "/MANIFEST:NO")
+ list(APPEND CLIENT_LINK_OPTIONS "/MANIFEST:NO")
+endif()
+
+set(CLIENT_EXECUTABLE_OPTIONS WIN32)
diff --git a/cmake/renderer_common.cmake b/cmake/renderer_common.cmake
new file mode 100644
index 00000000..01106c61
--- /dev/null
+++ b/cmake/renderer_common.cmake
@@ -0,0 +1,37 @@
+include_guard(GLOBAL)
+
+set(RENDERER_COMMON_SOURCES
+ ${SOURCE_DIR}/renderercommon/tr_font.c
+ ${SOURCE_DIR}/renderercommon/tr_image_bmp.c
+ ${SOURCE_DIR}/renderercommon/tr_image_jpg.c
+ ${SOURCE_DIR}/renderercommon/tr_image_pcx.c
+ ${SOURCE_DIR}/renderercommon/tr_image_png.c
+ ${SOURCE_DIR}/renderercommon/tr_image_tga.c
+ ${SOURCE_DIR}/renderercommon/tr_noise.c
+ ${SOURCE_DIR}/renderercommon/puff.c
+)
+
+set(SDL_RENDERER_SOURCES
+ ${SOURCE_DIR}/sdl/sdl_gamma.c
+ ${SOURCE_DIR}/sdl/sdl_glimp.c
+)
+
+set(DYNAMIC_RENDERER_SOURCES
+ ${SOURCE_DIR}/renderercommon/tr_subs.c
+ ${SOURCE_DIR}/qcommon/q_shared.c
+ ${SOURCE_DIR}/qcommon/q_math.c
+)
+
+if(USE_FREETYPE)
+ list(APPEND RENDERER_DEFINITIONS BUILD_FREETYPE)
+endif()
+
+if(USE_RENDERER_DLOPEN)
+ list(APPEND RENDERER_DEFINITIONS USE_RENDERER_DLOPEN)
+elseif(BUILD_RENDERER_GL1 AND BUILD_RENDERER_GL2)
+ message(FATAL_ERROR "Multiple static renderers enabled; choose one")
+elseif(NOT BUILD_RENDERER_GL1 AND NOT BUILD_RENDERER_GL2)
+ message(FATAL_ERROR "Zero static renderers enabled; choose one")
+endif()
+
+list(APPEND RENDERER_LIBRARIES ${COMMON_LIBRARIES})
diff --git a/cmake/renderer_gl1.cmake b/cmake/renderer_gl1.cmake
new file mode 100644
index 00000000..8f1b93f0
--- /dev/null
+++ b/cmake/renderer_gl1.cmake
@@ -0,0 +1,62 @@
+if(NOT BUILD_RENDERER_GL1)
+ return()
+endif()
+
+include(utils/arch)
+include(utils/set_output_dirs)
+include(renderer_common)
+
+set(RENDERER_GL1_SOURCES
+ ${SOURCE_DIR}/renderergl1/tr_altivec.c
+ ${SOURCE_DIR}/renderergl1/tr_animation.c
+ ${SOURCE_DIR}/renderergl1/tr_backend.c
+ ${SOURCE_DIR}/renderergl1/tr_bsp.c
+ ${SOURCE_DIR}/renderergl1/tr_cmds.c
+ ${SOURCE_DIR}/renderergl1/tr_curve.c
+ ${SOURCE_DIR}/renderergl1/tr_flares.c
+ ${SOURCE_DIR}/renderergl1/tr_image.c
+ ${SOURCE_DIR}/renderergl1/tr_init.c
+ ${SOURCE_DIR}/renderergl1/tr_light.c
+ ${SOURCE_DIR}/renderergl1/tr_main.c
+ ${SOURCE_DIR}/renderergl1/tr_marks.c
+ ${SOURCE_DIR}/renderergl1/tr_mesh.c
+ ${SOURCE_DIR}/renderergl1/tr_model.c
+ ${SOURCE_DIR}/renderergl1/tr_model_iqm.c
+ ${SOURCE_DIR}/renderergl1/tr_scene.c
+ ${SOURCE_DIR}/renderergl1/tr_shade.c
+ ${SOURCE_DIR}/renderergl1/tr_shade_calc.c
+ ${SOURCE_DIR}/renderergl1/tr_shader.c
+ ${SOURCE_DIR}/renderergl1/tr_shadows.c
+ ${SOURCE_DIR}/renderergl1/tr_sky.c
+ ${SOURCE_DIR}/renderergl1/tr_surface.c
+ ${SOURCE_DIR}/renderergl1/tr_world.c
+)
+
+set(RENDERER_GL1_BASENAME renderer_opengl1)
+
+if(USE_ARCHLESS_FILENAMES)
+ set(RENDERER_GL1_BINARY ${RENDERER_GL1_BASENAME})
+ list(APPEND RENDERER_DEFINITIONS USE_ARCHLESS_FILENAMES)
+else()
+ set(RENDERER_GL1_BINARY ${RENDERER_GL1_BASENAME}_${ARCH})
+endif()
+
+list(APPEND RENDERER_GL1_BINARY_SOURCES
+ ${RENDERER_COMMON_SOURCES}
+ ${RENDERER_GL1_SOURCES}
+ ${SDL_RENDERER_SOURCES}
+ ${RENDERER_LIBRARY_SOURCES})
+
+if(USE_RENDERER_DLOPEN)
+ list(APPEND RENDERER_GL1_BINARY_SOURCES ${DYNAMIC_RENDERER_SOURCES})
+
+ add_library(${RENDERER_GL1_BINARY} SHARED ${RENDERER_GL1_BINARY_SOURCES})
+
+ target_link_libraries( ${RENDERER_GL1_BINARY} PRIVATE ${RENDERER_LIBRARIES})
+ target_include_directories( ${RENDERER_GL1_BINARY} PRIVATE ${RENDERER_INCLUDE_DIRS})
+ target_compile_definitions( ${RENDERER_GL1_BINARY} PRIVATE ${RENDERER_DEFINITIONS})
+ target_compile_options( ${RENDERER_GL1_BINARY} PRIVATE ${RENDERER_COMPILE_OPTIONS})
+ target_link_options( ${RENDERER_GL1_BINARY} PRIVATE ${RENDERER_LINK_OPTIONS})
+
+ set_output_dirs(${RENDERER_GL1_BINARY})
+endif()
diff --git a/cmake/renderer_gl2.cmake b/cmake/renderer_gl2.cmake
new file mode 100644
index 00000000..78daec11
--- /dev/null
+++ b/cmake/renderer_gl2.cmake
@@ -0,0 +1,94 @@
+if(NOT BUILD_RENDERER_GL2)
+ return()
+endif()
+
+include(utils/arch)
+include(utils/set_output_dirs)
+include(renderer_common)
+
+set(RENDERER_GL2_SOURCES
+ ${SOURCE_DIR}/renderergl2/tr_animation.c
+ ${SOURCE_DIR}/renderergl2/tr_backend.c
+ ${SOURCE_DIR}/renderergl2/tr_bsp.c
+ ${SOURCE_DIR}/renderergl2/tr_cmds.c
+ ${SOURCE_DIR}/renderergl2/tr_curve.c
+ ${SOURCE_DIR}/renderergl2/tr_dsa.c
+ ${SOURCE_DIR}/renderergl2/tr_extramath.c
+ ${SOURCE_DIR}/renderergl2/tr_extensions.c
+ ${SOURCE_DIR}/renderergl2/tr_fbo.c
+ ${SOURCE_DIR}/renderergl2/tr_flares.c
+ ${SOURCE_DIR}/renderergl2/tr_glsl.c
+ ${SOURCE_DIR}/renderergl2/tr_image.c
+ ${SOURCE_DIR}/renderergl2/tr_image_dds.c
+ ${SOURCE_DIR}/renderergl2/tr_init.c
+ ${SOURCE_DIR}/renderergl2/tr_light.c
+ ${SOURCE_DIR}/renderergl2/tr_main.c
+ ${SOURCE_DIR}/renderergl2/tr_marks.c
+ ${SOURCE_DIR}/renderergl2/tr_mesh.c
+ ${SOURCE_DIR}/renderergl2/tr_model.c
+ ${SOURCE_DIR}/renderergl2/tr_model_iqm.c
+ ${SOURCE_DIR}/renderergl2/tr_postprocess.c
+ ${SOURCE_DIR}/renderergl2/tr_scene.c
+ ${SOURCE_DIR}/renderergl2/tr_shade.c
+ ${SOURCE_DIR}/renderergl2/tr_shade_calc.c
+ ${SOURCE_DIR}/renderergl2/tr_shader.c
+ ${SOURCE_DIR}/renderergl2/tr_shadows.c
+ ${SOURCE_DIR}/renderergl2/tr_sky.c
+ ${SOURCE_DIR}/renderergl2/tr_surface.c
+ ${SOURCE_DIR}/renderergl2/tr_vbo.c
+ ${SOURCE_DIR}/renderergl2/tr_world.c
+)
+
+file(GLOB RENDERER_GL2_SHADER_SOURCES ${SOURCE_DIR}/renderergl2/glsl/*.glsl)
+
+set(SHADERS_DIR ${CMAKE_BINARY_DIR}/shaders.dir)
+file(MAKE_DIRECTORY ${SHADERS_DIR})
+
+foreach(SHADER_FILE IN LISTS RENDERER_GL2_SHADER_SOURCES)
+ get_filename_component(SHADER_NAME ${SHADER_FILE} NAME_WE)
+ set(SHADER_C_FILE ${SHADERS_DIR}/${SHADER_NAME}.c)
+
+ string(REPLACE "${CMAKE_BINARY_DIR}/" "" SHADER_C_FILE_COMMENT ${SHADER_C_FILE})
+
+ add_custom_command(
+ OUTPUT ${SHADER_C_FILE}
+ COMMAND ${CMAKE_COMMAND}
+ -DINPUT_FILE=${SHADER_FILE}
+ -DOUTPUT_FILE=${SHADER_C_FILE}
+ -DSHADER_NAME=${SHADER_NAME}
+ -P ${CMAKE_SOURCE_DIR}/cmake/utils/stringify_shader.cmake
+ DEPENDS ${SHADER_FILE}
+ COMMENT "Stringify shader ${SHADER_C_FILE_COMMENT}")
+
+ list(APPEND RENDERER_GL2_SHADER_C_SOURCES ${SHADER_C_FILE})
+endforeach()
+
+set(RENDERER_GL2_BASENAME renderer_opengl2)
+
+if(USE_ARCHLESS_FILENAMES)
+ set(RENDERER_GL2_BINARY ${RENDERER_GL2_BASENAME})
+ list(APPEND RENDERER_DEFINITIONS USE_ARCHLESS_FILENAMES)
+else()
+ set(RENDERER_GL2_BINARY ${RENDERER_GL2_BASENAME}_${ARCH})
+endif()
+
+list(APPEND RENDERER_GL2_BINARY_SOURCES
+ ${RENDERER_COMMON_SOURCES}
+ ${RENDERER_GL2_SOURCES}
+ ${RENDERER_GL2_SHADER_C_SOURCES}
+ ${SDL_RENDERER_SOURCES}
+ ${RENDERER_LIBRARY_SOURCES})
+
+if(USE_RENDERER_DLOPEN)
+ list(APPEND RENDERER_GL2_BINARY_SOURCES ${DYNAMIC_RENDERER_SOURCES})
+
+ add_library(${RENDERER_GL2_BINARY} SHARED ${RENDERER_GL2_BINARY_SOURCES})
+
+ target_link_libraries( ${RENDERER_GL2_BINARY} PRIVATE ${RENDERER_LIBRARIES})
+ target_include_directories( ${RENDERER_GL2_BINARY} PRIVATE ${RENDERER_INCLUDE_DIRS})
+ target_compile_definitions( ${RENDERER_GL2_BINARY} PRIVATE ${RENDERER_DEFINITIONS})
+ target_compile_options( ${RENDERER_GL2_BINARY} PRIVATE ${RENDERER_COMPILE_OPTIONS})
+ target_link_options( ${RENDERER_GL2_BINARY} PRIVATE ${RENDERER_LINK_OPTIONS})
+
+ set_output_dirs(${RENDERER_GL2_BINARY})
+endif()
diff --git a/cmake/server.cmake b/cmake/server.cmake
new file mode 100644
index 00000000..5a06e42d
--- /dev/null
+++ b/cmake/server.cmake
@@ -0,0 +1,54 @@
+
+if(NOT BUILD_SERVER)
+ return()
+endif()
+
+include(utils/set_output_dirs)
+include(shared_sources)
+
+set(NULL_SOURCES
+ ${SOURCE_DIR}/null/null_client.c
+ ${SOURCE_DIR}/null/null_input.c
+ ${SOURCE_DIR}/null/null_snddma.c
+)
+
+if(USE_ARCHLESS_FILENAMES)
+ set(SERVER_BINARY ${SERVER_NAME})
+ list(APPEND SERVER_DEFINITIONS USE_ARCHLESS_FILENAMES)
+else()
+ set(SERVER_BINARY ${SERVER_NAME}.${ARCH})
+endif()
+
+list(APPEND SERVER_DEFINITIONS DEDICATED)
+list(APPEND SERVER_DEFINITIONS BOTLIB)
+
+if(BUILD_STANDALONE)
+ list(APPEND SERVER_DEFINITIONS STANDALONE)
+endif()
+
+if(USE_VOIP)
+ list(APPEND SERVER_DEFINITIONS USE_VOIP)
+endif()
+
+list(APPEND SERVER_BINARY_SOURCES
+ ${SERVER_SOURCES}
+ ${NULL_SOURCES}
+ ${COMMON_SOURCES}
+ ${BOTLIB_SOURCES}
+ ${SYSTEM_SOURCES}
+ ${ASM_SOURCES}
+ ${SERVER_LIBRARY_SOURCES})
+
+add_executable(${SERVER_BINARY} ${SERVER_EXECUTABLE_OPTIONS} ${SERVER_BINARY_SOURCES})
+
+target_include_directories( ${SERVER_BINARY} PRIVATE ${SERVER_INCLUDE_DIRS})
+target_compile_definitions( ${SERVER_BINARY} PRIVATE ${SERVER_DEFINITIONS})
+target_compile_options( ${SERVER_BINARY} PRIVATE ${SERVER_COMPILE_OPTIONS})
+target_link_libraries( ${SERVER_BINARY} PRIVATE ${COMMON_LIBRARIES} ${SERVER_LIBRARIES})
+target_link_options( ${SERVER_BINARY} PRIVATE ${SERVER_LINK_OPTIONS})
+
+set_output_dirs(${SERVER_BINARY})
+
+if(POST_SERVER_CONFIGURE_FUNCTION)
+ cmake_language(CALL ${POST_SERVER_CONFIGURE_FUNCTION})
+endif()
diff --git a/cmake/shared_sources.cmake b/cmake/shared_sources.cmake
new file mode 100644
index 00000000..b385d663
--- /dev/null
+++ b/cmake/shared_sources.cmake
@@ -0,0 +1,104 @@
+include_guard(GLOBAL)
+
+include(utils/add_git_dependency)
+include(utils/arch)
+include(utils/disable_warnings)
+
+set(COMMON_SOURCES
+ ${SOURCE_DIR}/qcommon/cm_load.c
+ ${SOURCE_DIR}/qcommon/cm_patch.c
+ ${SOURCE_DIR}/qcommon/cm_polylib.c
+ ${SOURCE_DIR}/qcommon/cm_test.c
+ ${SOURCE_DIR}/qcommon/cm_trace.c
+ ${SOURCE_DIR}/qcommon/cmd.c
+ ${SOURCE_DIR}/qcommon/common.c
+ ${SOURCE_DIR}/qcommon/cvar.c
+ ${SOURCE_DIR}/qcommon/files.c
+ ${SOURCE_DIR}/qcommon/md4.c
+ ${SOURCE_DIR}/qcommon/md5.c
+ ${SOURCE_DIR}/qcommon/msg.c
+ ${SOURCE_DIR}/qcommon/net_chan.c
+ ${SOURCE_DIR}/qcommon/net_ip.c
+ ${SOURCE_DIR}/qcommon/huffman.c
+ ${SOURCE_DIR}/qcommon/q_math.c
+ ${SOURCE_DIR}/qcommon/q_shared.c
+ ${SOURCE_DIR}/qcommon/unzip.c
+ ${SOURCE_DIR}/qcommon/ioapi.c
+ ${SOURCE_DIR}/qcommon/vm.c
+ ${SOURCE_DIR}/qcommon/vm_interpreted.c
+)
+
+disable_warnings(
+ ${SOURCE_DIR}/qcommon/unzip.c
+ ${SOURCE_DIR}/qcommon/ioapi.c
+)
+
+add_git_dependency(${SOURCE_DIR}/qcommon/common.c)
+
+if(ARCH MATCHES "x86" OR ARCH MATCHES "x86_64")
+ list(APPEND COMMON_SOURCES
+ ${SOURCE_DIR}/qcommon/vm_x86.c
+ )
+elseif(ARCH MATCHES "ppc" OR ARCH MATCHES "ppc64")
+ list(APPEND COMMON_SOURCES
+ ${SOURCE_DIR}/qcommon/vm_powerpc.c
+ ${SOURCE_DIR}/qcommon/vm_powerpc_asm.c
+ )
+elseif(ARCH MATCHES "arm")
+ list(APPEND COMMON_SOURCES
+ ${SOURCE_DIR}/qcommon/vm_armv71.c
+ )
+else()
+ list(APPEND SERVER_DEFINITIONS NO_VM_COMPILED)
+ list(APPEND CLIENT_DEFINITIONS NO_VM_COMPILED)
+endif()
+
+set(SYSTEM_SOURCES
+ ${SOURCE_DIR}/sys/con_log.c
+ ${SOURCE_DIR}/sys/sys_autoupdater.c
+ ${SOURCE_DIR}/sys/sys_main.c
+ ${SYSTEM_PLATFORM_SOURCES}
+)
+
+set(SERVER_SOURCES
+ ${SOURCE_DIR}/server/sv_bot.c
+ ${SOURCE_DIR}/server/sv_client.c
+ ${SOURCE_DIR}/server/sv_ccmds.c
+ ${SOURCE_DIR}/server/sv_game.c
+ ${SOURCE_DIR}/server/sv_init.c
+ ${SOURCE_DIR}/server/sv_main.c
+ ${SOURCE_DIR}/server/sv_net_chan.c
+ ${SOURCE_DIR}/server/sv_snapshot.c
+ ${SOURCE_DIR}/server/sv_world.c
+)
+
+set(BOTLIB_SOURCES
+ ${SOURCE_DIR}/botlib/be_aas_bspq3.c
+ ${SOURCE_DIR}/botlib/be_aas_cluster.c
+ ${SOURCE_DIR}/botlib/be_aas_debug.c
+ ${SOURCE_DIR}/botlib/be_aas_entity.c
+ ${SOURCE_DIR}/botlib/be_aas_file.c
+ ${SOURCE_DIR}/botlib/be_aas_main.c
+ ${SOURCE_DIR}/botlib/be_aas_move.c
+ ${SOURCE_DIR}/botlib/be_aas_optimize.c
+ ${SOURCE_DIR}/botlib/be_aas_reach.c
+ ${SOURCE_DIR}/botlib/be_aas_route.c
+ ${SOURCE_DIR}/botlib/be_aas_routealt.c
+ ${SOURCE_DIR}/botlib/be_aas_sample.c
+ ${SOURCE_DIR}/botlib/be_ai_char.c
+ ${SOURCE_DIR}/botlib/be_ai_chat.c
+ ${SOURCE_DIR}/botlib/be_ai_gen.c
+ ${SOURCE_DIR}/botlib/be_ai_goal.c
+ ${SOURCE_DIR}/botlib/be_ai_move.c
+ ${SOURCE_DIR}/botlib/be_ai_weap.c
+ ${SOURCE_DIR}/botlib/be_ai_weight.c
+ ${SOURCE_DIR}/botlib/be_ea.c
+ ${SOURCE_DIR}/botlib/be_interface.c
+ ${SOURCE_DIR}/botlib/l_crc.c
+ ${SOURCE_DIR}/botlib/l_libvar.c
+ ${SOURCE_DIR}/botlib/l_log.c
+ ${SOURCE_DIR}/botlib/l_memory.c
+ ${SOURCE_DIR}/botlib/l_precomp.c
+ ${SOURCE_DIR}/botlib/l_script.c
+ ${SOURCE_DIR}/botlib/l_struct.c
+)
diff --git a/cmake/tools/CMakeLists.txt b/cmake/tools/CMakeLists.txt
new file mode 100644
index 00000000..4fbf0dd9
--- /dev/null
+++ b/cmake/tools/CMakeLists.txt
@@ -0,0 +1,86 @@
+cmake_minimum_required(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION})
+project(qvm_tools LANGUAGES C)
+
+include(utils/set_output_dirs)
+
+set(Q3ASM_SOURCES
+ ${SOURCE_DIR}/tools/asm/q3asm.c
+ ${SOURCE_DIR}/tools/asm/cmdlib.c
+)
+
+set(Q3LCC_SOURCES
+ ${SOURCE_DIR}/tools/lcc/etc/lcc.c
+ ${SOURCE_DIR}/tools/lcc/etc/bytecode.c
+)
+
+set(Q3RCC_SOURCES
+ ${SOURCE_DIR}/tools/lcc/src/alloc.c
+ ${SOURCE_DIR}/tools/lcc/src/bind.c
+ ${SOURCE_DIR}/tools/lcc/src/bytecode.c
+ ${SOURCE_DIR}/tools/lcc/src/dag.c
+ ${SOURCE_DIR}/tools/lcc/src/decl.c
+ ${SOURCE_DIR}/tools/lcc/src/enode.c
+ ${SOURCE_DIR}/tools/lcc/src/error.c
+ ${SOURCE_DIR}/tools/lcc/src/event.c
+ ${SOURCE_DIR}/tools/lcc/src/expr.c
+ ${SOURCE_DIR}/tools/lcc/src/gen.c
+ ${SOURCE_DIR}/tools/lcc/src/init.c
+ ${SOURCE_DIR}/tools/lcc/src/inits.c
+ ${SOURCE_DIR}/tools/lcc/src/input.c
+ ${SOURCE_DIR}/tools/lcc/src/lex.c
+ ${SOURCE_DIR}/tools/lcc/src/list.c
+ ${SOURCE_DIR}/tools/lcc/src/main.c
+ ${SOURCE_DIR}/tools/lcc/src/null.c
+ ${SOURCE_DIR}/tools/lcc/src/output.c
+ ${SOURCE_DIR}/tools/lcc/src/prof.c
+ ${SOURCE_DIR}/tools/lcc/src/profio.c
+ ${SOURCE_DIR}/tools/lcc/src/simp.c
+ ${SOURCE_DIR}/tools/lcc/src/stmt.c
+ ${SOURCE_DIR}/tools/lcc/src/string.c
+ ${SOURCE_DIR}/tools/lcc/src/sym.c
+ ${SOURCE_DIR}/tools/lcc/src/symbolic.c
+ ${SOURCE_DIR}/tools/lcc/src/trace.c
+ ${SOURCE_DIR}/tools/lcc/src/tree.c
+ ${SOURCE_DIR}/tools/lcc/src/types.c
+)
+
+set(Q3RCC_DAGCHECK_SOURCE ${SOURCE_DIR}/tools/lcc/src/dagcheck.md)
+
+set(Q3CPP_SOURCES
+ ${SOURCE_DIR}/tools/lcc/cpp/cpp.c
+ ${SOURCE_DIR}/tools/lcc/cpp/lex.c
+ ${SOURCE_DIR}/tools/lcc/cpp/nlist.c
+ ${SOURCE_DIR}/tools/lcc/cpp/tokens.c
+ ${SOURCE_DIR}/tools/lcc/cpp/macro.c
+ ${SOURCE_DIR}/tools/lcc/cpp/eval.c
+ ${SOURCE_DIR}/tools/lcc/cpp/include.c
+ ${SOURCE_DIR}/tools/lcc/cpp/hideset.c
+ ${SOURCE_DIR}/tools/lcc/cpp/getopt.c
+ ${SOURCE_DIR}/tools/lcc/cpp/unix.c
+)
+
+set(LBURG_SOURCES
+ ${SOURCE_DIR}/tools/lcc/lburg/lburg.c
+ ${SOURCE_DIR}/tools/lcc/lburg/gram.c
+)
+
+add_executable(q3asm ${Q3ASM_SOURCES})
+set_output_dirs(q3asm)
+add_executable(q3lcc ${Q3LCC_SOURCES})
+set_output_dirs(q3lcc)
+add_dependencies(q3lcc q3rcc q3cpp)
+
+add_executable(lburg ${LBURG_SOURCES})
+set_output_dirs(lburg)
+set(DAGCHECK_C ${CMAKE_BINARY_DIR}/dagcheck.c)
+add_custom_command(
+ OUTPUT ${DAGCHECK_C}
+ COMMAND lburg ${Q3RCC_DAGCHECK_SOURCE} ${DAGCHECK_C}
+ DEPENDS lburg ${Q3RCC_DAGCHECK_SOURCE})
+
+add_executable(q3rcc ${Q3RCC_SOURCES} ${DAGCHECK_C})
+set_output_dirs(q3rcc)
+target_include_directories(q3rcc PRIVATE ${SOURCE_DIR}/tools/lcc/src)
+
+add_executable(q3cpp ${Q3CPP_SOURCES})
+set_output_dirs(q3cpp)
diff --git a/cmake/utils/add_git_dependency.cmake b/cmake/utils/add_git_dependency.cmake
new file mode 100644
index 00000000..30cec055
--- /dev/null
+++ b/cmake/utils/add_git_dependency.cmake
@@ -0,0 +1,27 @@
+include_guard(GLOBAL)
+
+function(add_git_dependency SOURCE_FILE)
+ set(GIT_DIR ${CMAKE_SOURCE_DIR}/.git)
+ if(NOT EXISTS ${GIT_DIR})
+ return()
+ endif()
+
+ set(GIT_FILES)
+ list(APPEND GIT_FILES ${GIT_DIR}/HEAD)
+ list(APPEND GIT_FILES ${GIT_DIR}/packed-refs)
+
+ file(READ ${GIT_DIR}/HEAD GIT_HEAD)
+ string(REGEX MATCH "^ref: (.+)$" HAVE_REF ${GIT_HEAD})
+ if(HAVE_REF)
+ set(GIT_REF_PATH ${CMAKE_MATCH_1})
+ string(STRIP ${GIT_REF_PATH} GIT_REF_PATH)
+ list(APPEND GIT_FILES ${GIT_DIR}/${GIT_REF_PATH})
+ endif()
+
+ foreach(GIT_FILE IN LISTS GIT_FILES)
+ if(EXISTS ${GIT_FILE})
+ set_source_files_properties(${SOURCE_FILE}
+ PROPERTIES OBJECT_DEPENDS ${GIT_FILE})
+ endif()
+ endforeach()
+endfunction()
diff --git a/cmake/utils/arch.cmake b/cmake/utils/arch.cmake
new file mode 100644
index 00000000..4915a3f6
--- /dev/null
+++ b/cmake/utils/arch.cmake
@@ -0,0 +1,23 @@
+include_guard(GLOBAL)
+
+set(DETECT_ARCH_C ${CMAKE_BINARY_DIR}/detect_arch.c)
+
+file(WRITE ${DETECT_ARCH_C}
+"#include \"${SOURCE_DIR}/qcommon/q_platform.h\"
+#include
+int main()
+{
+ puts(ARCH_STRING);
+ return 0;
+}
+")
+
+try_run(RUN_EXITCODE COMPILE_SUCCESS
+ ${CMAKE_BINARY_DIR} ${DETECT_ARCH_C}
+ RUN_OUTPUT_VARIABLE ARCH)
+
+string(STRIP ${ARCH} ARCH)
+
+if(NOT COMPILE_SUCCESS OR RUN_EXITCODE OR NOT ARCH)
+ message(FATAL_ERROR "Architecture detection failed")
+endif()
diff --git a/cmake/utils/disable_warnings.cmake b/cmake/utils/disable_warnings.cmake
new file mode 100644
index 00000000..061a65af
--- /dev/null
+++ b/cmake/utils/disable_warnings.cmake
@@ -0,0 +1,17 @@
+include_guard(GLOBAL)
+
+function(disable_warnings)
+ set(SOURCES ${ARGN})
+
+ foreach(FILE IN LISTS SOURCES)
+ if(MSVC)
+ # Annoyingly if you disable all warnings (/w) in combination with enabling
+ # some warnings, which we and/or CMake inevitably do, this causes a
+ # meta-warning D9025, so instead we have to individually disable them:
+ set_source_files_properties(${FILE} PROPERTIES COMPILE_FLAGS
+ "/wd4131 /wd4245 /wd4100 /wd4127 /wd4244 /wd4310 /wd4457 /wd4456 /wd4701 /wd4305 /wd4189 /wd4232")
+ else()
+ set_source_files_properties(${FILE} PROPERTIES COMPILE_FLAGS -w)
+ endif()
+ endforeach()
+endfunction()
diff --git a/cmake/utils/find_include_dirs.cmake b/cmake/utils/find_include_dirs.cmake
new file mode 100644
index 00000000..4ea1aa9e
--- /dev/null
+++ b/cmake/utils/find_include_dirs.cmake
@@ -0,0 +1,37 @@
+include_guard(GLOBAL)
+
+function(find_include_dirs OUT_VAR)
+ set(SOURCES ${ARGN})
+
+ # Get top most common directory prefix for all source files
+ set(COMMON_PATH "")
+ foreach(FILE IN LISTS SOURCES)
+ get_filename_component(DIR ${FILE} DIRECTORY)
+ file(REAL_PATH ${DIR} DIR)
+ if(COMMON_PATH STREQUAL "")
+ set(COMMON_PATH ${DIR})
+ else()
+ string(LENGTH ${COMMON_PATH} PREFIX_LEN)
+ while(NOT ${DIR} MATCHES "^${COMMON_PATH}(/|$)" AND PREFIX_LEN GREATER 0)
+ string(SUBSTRING ${COMMON_PATH} 0 ${PREFIX_LEN} COMMON_PATH)
+ math(EXPR PREFIX_LEN "${PREFIX_LEN} - 1")
+ endwhile()
+ endif()
+ endforeach()
+
+ if(NOT IS_DIRECTORY ${COMMON_PATH})
+ message(FATAL_ERROR "Could not determine common directory for source files")
+ endif()
+
+ # Recursively find directories that contain .h files under common directory
+ file(GLOB_RECURSE HEADER_FILES ${COMMON_PATH}/*.h)
+ set(INCLUDE_DIRS "")
+ foreach(HEADER_FILE IN LISTS HEADER_FILES)
+ get_filename_component(HEADER_DIR ${HEADER_FILE} DIRECTORY)
+ list(APPEND INCLUDE_DIRS ${HEADER_DIR})
+ endforeach()
+
+ list(REMOVE_DUPLICATES INCLUDE_DIRS)
+
+ set(${OUT_VAR} ${INCLUDE_DIRS} PARENT_SCOPE)
+endfunction()
diff --git a/cmake/utils/qvm_tools.cmake b/cmake/utils/qvm_tools.cmake
new file mode 100644
index 00000000..cf299ca3
--- /dev/null
+++ b/cmake/utils/qvm_tools.cmake
@@ -0,0 +1,83 @@
+include_guard(GLOBAL)
+
+if(NOT BUILD_GAME_QVMS)
+ return()
+endif()
+
+include(ExternalProject)
+
+set(TOOLS_DIR ${CMAKE_BINARY_DIR}/tools)
+
+if(CMAKE_BUILD_TYPE)
+ set(BUILD_TYPE_ARG -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE})
+endif()
+
+ExternalProject_Add(qvm_tools
+ SOURCE_DIR ${CMAKE_SOURCE_DIR}/cmake/tools
+ BINARY_DIR ${TOOLS_DIR}
+ CMAKE_ARGS
+ -DSOURCE_DIR=${SOURCE_DIR}
+ -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}
+ -DCMAKE_MINIMUM_REQUIRED_VERSION=${CMAKE_MINIMUM_REQUIRED_VERSION}
+ ${BUILD_TYPE_ARG}
+ INSTALL_COMMAND "")
+
+set(Q3LCC ${TOOLS_DIR}/$/q3lcc)
+set(Q3ASM ${TOOLS_DIR}/$/q3asm)
+
+function(add_qvm MODULE_NAME)
+ list(REMOVE_AT ARGV 0)
+ cmake_parse_arguments(ARG "" "" "DEFINITIONS;OUTPUT_NAME;OUTPUT_DIRECTORY;SOURCES" ${ARGV})
+
+ set(QVM_OUTPUT_DIR ${CMAKE_BINARY_DIR}/$)
+ if(ARG_OUTPUT_DIRECTORY)
+ set(QVM_OUTPUT_DIR ${QVM_OUTPUT_DIR}/${ARG_OUTPUT_DIRECTORY})
+ endif()
+ add_custom_command(
+ OUTPUT ${QVM_OUTPUT_DIR}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${QVM_OUTPUT_DIR})
+
+ if(ARG_OUTPUT_NAME)
+ set(QVM_FILE ${QVM_OUTPUT_DIR}/${ARG_OUTPUT_NAME}.qvm)
+ else()
+ set(QVM_FILE ${QVM_OUTPUT_DIR}/${MODULE_NAME}.qvm)
+ endif()
+
+ set(QVM_ASM_DIR ${CMAKE_BINARY_DIR}/qvm.dir/${MODULE_NAME})
+ file(MAKE_DIRECTORY ${QVM_ASM_DIR})
+
+ set(LCC_FLAGS "")
+ foreach(DEFINITION IN LISTS ARG_DEFINITIONS)
+ list(APPEND LCC_FLAGS "-D${DEFINITION}")
+ endforeach()
+
+ set(ASM_FILES "")
+ foreach(SOURCE ${ARG_SOURCES})
+ if(${SOURCE} MATCHES "\\.asm$")
+ list(APPEND ASM_FILES ${SOURCE})
+ continue()
+ endif()
+
+ get_filename_component(BASE_FILE ${SOURCE} NAME_WE)
+ set(ASM_FILE ${QVM_ASM_DIR}/${BASE_FILE}.asm)
+ string(REPLACE "${CMAKE_BINARY_DIR}/" "" ASM_FILE_COMMENT ${ASM_FILE})
+
+ add_custom_command(
+ OUTPUT ${ASM_FILE}
+ COMMAND ${Q3LCC} ${LCC_FLAGS} -o ${ASM_FILE} ${SOURCE}
+ DEPENDS ${SOURCE} qvm_tools
+ COMMENT "Building C object ${ASM_FILE_COMMENT}")
+
+ list(APPEND ASM_FILES ${ASM_FILE})
+ endforeach()
+
+ string(REPLACE "${CMAKE_BINARY_DIR}/" "" QVM_FILE_COMMENT ${QVM_FILE})
+ add_custom_command(
+ OUTPUT ${QVM_FILE}
+ COMMAND ${Q3ASM} -o ${QVM_FILE} ${ASM_FILES}
+ DEPENDS ${ASM_FILES} qvm_tools
+ COMMENT "Linking C QVM library ${QVM_FILE_COMMENT}")
+
+ string(REGEX REPLACE "[^A-Za-z0-9]" "_" TARGET_NAME ${MODULE_NAME})
+ add_custom_target(${TARGET_NAME} ALL DEPENDS ${QVM_FILE})
+endfunction()
diff --git a/cmake/utils/set_output_dirs.cmake b/cmake/utils/set_output_dirs.cmake
new file mode 100644
index 00000000..a38ecc03
--- /dev/null
+++ b/cmake/utils/set_output_dirs.cmake
@@ -0,0 +1,27 @@
+include_guard(GLOBAL)
+
+function(set_output_dirs TARGET)
+ list(REMOVE_AT ARGV 0)
+ cmake_parse_arguments(ARG "" "" "SUBDIRECTORY" ${ARGV})
+
+ if(CMAKE_CONFIGURATION_TYPES) # Multi-config
+ set(CONFIGS ${CMAKE_CONFIGURATION_TYPES})
+ else() # Single-config
+ set(CONFIGS ${CMAKE_BUILD_TYPE})
+ endif()
+
+ foreach(CONFIG ${CONFIGS})
+ string(TOUPPER ${CONFIG} CONFIG_UPPER)
+
+ if(ARG_SUBDIRECTORY)
+ set(OUT_DIR ${CMAKE_BINARY_DIR}/${CONFIG}/${ARG_SUBDIRECTORY})
+ else()
+ set(OUT_DIR ${CMAKE_BINARY_DIR}/${CONFIG})
+ endif()
+
+ set_target_properties(${TARGET} PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY_${CONFIG_UPPER} ${OUT_DIR}
+ RUNTIME_OUTPUT_DIRECTORY_${CONFIG_UPPER} ${OUT_DIR}
+ ARCHIVE_OUTPUT_DIRECTORY_${CONFIG_UPPER} ${OUT_DIR})
+ endforeach()
+endfunction()
diff --git a/cmake/utils/stringify_shader.cmake b/cmake/utils/stringify_shader.cmake
new file mode 100644
index 00000000..71cc76be
--- /dev/null
+++ b/cmake/utils/stringify_shader.cmake
@@ -0,0 +1,13 @@
+# Convert a shader file to a compilable C file
+
+# INPUT_FILE, OUTPUT_FILE, SHADER_NAME must be set via -D
+
+file(READ ${INPUT_FILE} CONTENTS)
+
+string(REPLACE "\\" "\\\\" CONTENTS "${CONTENTS}") # Escape backslashes
+string(REPLACE "\"" "\\\"" CONTENTS "${CONTENTS}") # Escape double quotes
+string(REPLACE "\n" "\\n\"\n\"" CONTENTS "${CONTENTS}") # Escape newlines
+
+set(OUTPUT_CONTENT "const char *fallbackShader_${SHADER_NAME} =\n\"${CONTENTS}\";\n")
+
+file(WRITE ${OUTPUT_FILE} "${OUTPUT_CONTENT}")