diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7d36c1fb63a46cffe860de94cc8994067c82bc51..80a3bdcd6798a48d10ea928d60ca3c4fb77bf353 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -131,7 +131,11 @@ if("${SRB2_CONFIG_SYSTEM_LIBRARIES}")
 	find_package(SDL2_mixer REQUIRED)
 	find_package(CURL REQUIRED)
 	find_package(OPENMPT REQUIRED)
-	find_package(GME REQUIRED)
+
+	# libgme defaults to "Nuked" YM2612 emulator, which is
+	# very SLOW. The system library probably uses the
+	# default so just always build it.
+	#find_package(GME REQUIRED)
 endif()
 
 if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR})
@@ -142,13 +146,6 @@ if ((${SRB2_USE_CCACHE}) AND (${CMAKE_C_COMPILER} MATCHES "clang"))
 	message(WARNING "Using clang and CCache: You may want to set environment variable CCACHE_CPP2=yes to prevent include errors during compile.")
 endif()
 
-# Add sources from Sourcefile
-function(target_sourcefile type)
-	file(STRINGS Sourcefile list
-		REGEX "[-0-9A-Za-z_]+\.${type}")
-	target_sources(SRB2SDL2 PRIVATE ${list})
-endfunction()
-
 # bitness check
 set(SRB2_SYSTEM_BITS 0)
 if(CMAKE_SIZEOF_VOID_P EQUAL 8)
@@ -167,7 +164,8 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
 set(CMAKE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
 
 # Set EXE names so the assets CMakeLists can refer to its target
-set(SRB2_SDL2_EXE_NAME srb2 CACHE STRING "Executable binary output name")
+set(SRB2_SDL2_EXE_NAME "" CACHE STRING "Override executable binary output name")
+set(SRB2_SDL2_EXE_SUFFIX "" CACHE STRING "Optional executable suffix, separated by an underscore")
 
 include_directories(${CMAKE_CURRENT_BINARY_DIR}/src)
 
@@ -175,11 +173,37 @@ add_subdirectory(src)
 add_subdirectory(assets)
 
 
-## config.h generation
 set(GIT_EXECUTABLE "git" CACHE FILEPATH "Path to git binary")
 include(GitUtilities)
-git_latest_commit(SRB2_COMP_COMMIT "${CMAKE_SOURCE_DIR}")
-git_current_branch(SRB2_GIT_BRANCH "${CMAKE_SOURCE_DIR}")
-set(SRB2_COMP_BRANCH "${SRB2_GIT_BRANCH}")
-set(SRB2_COMP_REVISION "${SRB2_COMP_COMMIT}")
-configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/config.h)
+
+if("${SRB2_SDL2_EXE_NAME}" STREQUAL "")
+	# cause a reconfigure if the branch changes
+	get_git_dir(SRB2_GIT_DIR)
+	configure_file("${SRB2_GIT_DIR}/HEAD" HEAD COPYONLY)
+
+	git_current_branch(SRB2_GIT_REVISION)
+
+	if("${SRB2_GIT_REVISION}" STREQUAL "")
+		# use abbreviated commit hash if on detached HEAD
+		git_latest_commit(SRB2_GIT_REVISION)
+	endif()
+
+	if("${CMAKE_SYSTEM_NAME}" MATCHES "Windows")
+		list(APPEND EXE_NAME_PARTS "srb2win")
+	elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
+		list(APPEND EXE_NAME_PARTS "lsdlsrb2")
+	else()
+		list(APPEND EXE_NAME_PARTS "srb2")
+	endif()
+
+	if(NOT "${SRB2_GIT_REVISION}" STREQUAL "master")
+		list(APPEND EXE_NAME_PARTS ${SRB2_GIT_REVISION})
+	endif()
+else()
+	list(APPEND EXE_NAME_PARTS ${SRB2_SDL2_EXE_NAME})
+endif()
+
+list(APPEND EXE_NAME_PARTS ${SRB2_SDL2_EXE_SUFFIX})
+
+list(JOIN EXE_NAME_PARTS "_" EXE_NAME)
+set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME ${EXE_NAME})
diff --git a/CMakePresets.json b/CMakePresets.json
new file mode 100644
index 0000000000000000000000000000000000000000..7713bb38516877d5e8e50cc46914a7f58c9c6d73
--- /dev/null
+++ b/CMakePresets.json
@@ -0,0 +1,29 @@
+{
+	"version": 3,
+	"configurePresets": [
+		{
+			"name": "default",
+			"description": "Build using default generator",
+			"binaryDir": "build",
+			"cacheVariables": {
+				"CMAKE_C_FLAGS": "-fdiagnostics-color",
+				"CMAKE_CXX_FLAGS": "-fdiagnostics-color",
+				"CMAKE_BUILD_TYPE": "RelWithDebInfo"
+			}
+		},
+		{
+			"name": "debug",
+			"description": "Build for development (no optimizations)",
+			"inherits": "default",
+			"cacheVariables": {
+				"CMAKE_BUILD_TYPE": "Debug"
+			}
+		}
+	],
+	"buildPresets": [
+		{
+			"name": "default",
+			"configurePreset": "default"
+		}
+	]
+}
diff --git a/alias-bootstrap.sh b/alias-bootstrap.sh
new file mode 100755
index 0000000000000000000000000000000000000000..f1a6ac4ed918db4391a39c9a3076dfcc38d2861b
--- /dev/null
+++ b/alias-bootstrap.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env sh
+
+# All these commands can be run from anywhere in the git
+# tree, not just the top level.
+
+# Usage: git cmake
+#
+# Same usage as standard CMake command.
+#
+git config 'alias.cmake' '!cmake'
+
+# Usage: git build <build preset> [options]
+# Usage: git build [options]
+#
+# In the second usage, when no preset is given, the
+# "default" build preset is used.
+#
+# Available options can be found by running:
+#
+#     git cmake --build
+#
+git config 'alias.build' '!p="${1##-*}"; [ "$p" ] && shift; git cmake --build --preset "${p:-default}"'
+
+# Usage: git crossmake
+#
+# Shortcut to i686-w64-mingw32-cmake (CMake cross
+# compiler)
+#
+git config 'alias.crossmake' '!i686-w64-mingw32-cmake'
diff --git a/cmake/Comptime.cmake b/cmake/Comptime.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..8388aed9ece077229a35d601ffd0679cb2ecd035
--- /dev/null
+++ b/cmake/Comptime.cmake
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
+
+set(CMAKE_BINARY_DIR "${BINARY_DIR}")
+set(CMAKE_CURRENT_BINARY_DIR "${BINARY_DIR}")
+
+# Set up CMAKE path
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/")
+
+include(GitUtilities)
+
+git_current_branch(SRB2_COMP_BRANCH)
+git_working_tree_dirty(SRB2_COMP_UNCOMMITTED)
+
+git_latest_commit(SRB2_COMP_REVISION)
+git_subject(subject)
+string(REGEX REPLACE "([\"\\])" "\\\\\\1" SRB2_COMP_NOTE "${subject}")
+
+if("${CMAKE_BUILD_TYPE}" STREQUAL "")
+	set(CMAKE_BUILD_TYPE None)
+endif()
+
+# These build types enable optimizations of some kind by default.
+set(optimized_build_types "MINSIZEREL;RELEASE;RELWITHDEBINFO")
+
+string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type)
+if("${build_type}" IN_LIST optimized_build_types)
+	set(SRB2_COMP_OPTIMIZED TRUE)
+else()
+	set(SRB2_COMP_OPTIMIZED FALSE)
+endif()
+
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/src/config.h")
diff --git a/cmake/Modules/GitUtilities.cmake b/cmake/Modules/GitUtilities.cmake
index d29e6b509dd27015f429c9e8f4de12e20fcc5b12..586c7b433ff31476fe2a4cf0d5634d36d2c997be 100644
--- a/cmake/Modules/GitUtilities.cmake
+++ b/cmake/Modules/GitUtilities.cmake
@@ -6,38 +6,54 @@ endif()
 
 set(__GitUtilities ON)
 
-function(git_describe variable path)
-	execute_process(COMMAND "${GIT_EXECUTABLE}" "describe"
-		WORKING_DIRECTORY "${path}"
-		RESULT_VARIABLE result
+macro(_git_command)
+	execute_process(
+		COMMAND "${GIT_EXECUTABLE}" ${ARGN}
+		WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
 		OUTPUT_VARIABLE output
 		ERROR_QUIET
 		OUTPUT_STRIP_TRAILING_WHITESPACE
 	)
+endmacro()
 
+macro(_git_easy_command)
+	_git_command(${ARGN})
 	set(${variable} "${output}" PARENT_SCOPE)
-endfunction()
+endmacro()
 
-function(git_current_branch variable path)
-	execute_process(COMMAND ${GIT_EXECUTABLE} "symbolic-ref" "--short" "HEAD"
-		WORKING_DIRECTORY "${path}"
-		RESULT_VARIABLE result
-		OUTPUT_VARIABLE output
-		ERROR_QUIET
-		OUTPUT_STRIP_TRAILING_WHITESPACE
-	)
+function(git_current_branch variable)
+	_git_command(symbolic-ref -q --short HEAD)
+
+	# If a detached head, a ref could still be resolved.
+	if("${output}" STREQUAL "")
+		_git_command(describe --all --exact-match)
+
+		# Get the ref, in the form heads/master or
+		# remotes/origin/master so isolate the final part.
+		string(REGEX REPLACE ".*/" "" output "${output}")
+	endif()
 
 	set(${variable} "${output}" PARENT_SCOPE)
 endfunction()
 
-function(git_latest_commit variable path)
-	execute_process(COMMAND ${GIT_EXECUTABLE} "rev-parse" "--short" "HEAD"
-		WORKING_DIRECTORY "${path}"
-		RESULT_VARIABLE result
-		OUTPUT_VARIABLE output
-		ERROR_QUIET
-		OUTPUT_STRIP_TRAILING_WHITESPACE
-	)
+function(git_latest_commit variable)
+	_git_easy_command(rev-parse --short HEAD)
+endfunction()
 
-	set(${variable} "${output}" PARENT_SCOPE)
-endfunction()
\ No newline at end of file
+function(git_working_tree_dirty variable)
+	_git_command(status --porcelain -uno)
+
+	if(output STREQUAL "")
+		set(${variable} FALSE PARENT_SCOPE)
+	else()
+		set(${variable} TRUE PARENT_SCOPE)
+	endif()
+endfunction()
+
+function(git_subject variable)
+	_git_easy_command(log -1 --format=%s)
+endfunction()
+
+function(get_git_dir variable)
+	_git_easy_command(rev-parse --git-dir)
+endfunction()
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 8cd0310137481882a4594a2d009038a3be50bf19..b926b3b7a3372d206df781fd65bbf7a947ddaef3 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,18 +1,149 @@
 include(clang-tidy-default)
 
-add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32)
+add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
+	comptime.c
+	md5.c
+	config.h.in
+	string.c
+	d_main.c
+	d_clisrv.c
+	d_net.c
+	d_netfil.c
+	d_netcmd.c
+	dehacked.c
+	deh_soc.c
+	deh_lua.c
+	deh_tables.c
+	z_zone.c
+	f_finale.c
+	f_wipe.c
+	g_demo.c
+	g_game.c
+	g_input.c
+	am_map.c
+	command.c
+	console.c
+	hu_stuff.c
+	i_time.c
+	y_inter.c
+	st_stuff.c
+	m_aatree.c
+	m_anigif.c
+	m_argv.c
+	m_bbox.c
+	m_cheat.c
+	m_cond.c
+	m_easing.c
+	m_fixed.c
+	m_menu.c
+	m_misc.c
+	m_perfstats.c
+	m_random.c
+	m_queue.c
+	info.c
+	p_ceilng.c
+	p_enemy.c
+	p_floor.c
+	p_inter.c
+	p_lights.c
+	p_map.c
+	p_maputl.c
+	p_mobj.c
+	p_polyobj.c
+	p_saveg.c
+	p_setup.c
+	p_sight.c
+	p_spec.c
+	p_telept.c
+	p_tick.c
+	p_user.c
+	p_slopes.c
+	tables.c
+	r_bsp.c
+	r_data.c
+	r_draw.c
+	r_fps.c
+	r_main.c
+	r_plane.c
+	r_segs.c
+	r_skins.c
+	r_sky.c
+	r_splats.c
+	r_things.c
+	r_bbox.c
+	r_textures.c
+	r_patch.c
+	r_patchrotation.c
+	r_picformats.c
+	r_portal.c
+	screen.c
+	taglist.c
+	v_video.c
+	s_sound.c
+	sounds.c
+	w_wad.c
+	filesrch.c
+	mserv.c
+	http-mserv.c
+	i_tcp.c
+	lzf.c
+	b_bot.c
+	u_list.c
+	lua_script.c
+	lua_baselib.c
+	lua_mathlib.c
+	lua_hooklib.c
+	lua_consolelib.c
+	lua_infolib.c
+	lua_mobjlib.c
+	lua_playerlib.c
+	lua_skinlib.c
+	lua_thinkerlib.c
+	lua_maplib.c
+	lua_taglib.c
+	lua_polyobjlib.c
+	lua_blockmaplib.c
+	lua_hudlib.c
+	lua_hudlib_drawlist.c
+	lua_inputlib.c
+)
+
+# This updates the modification time for comptime.c at the
+# end of building so when the build system is ran next time,
+# that file gets flagged. comptime.c will always be rebuilt.
+#
+# This begs the question, why always rebuild comptime.c?
+# Some things like the git commit must be checked each time
+# the program is built. But the build system determines which
+# files should be rebuilt before anything else. So
+# comptime.c, which only needs to be rebuilt based on
+# information known at build time, must be told to rebuild
+# before that information can be ascertained.
+add_custom_command(
+	TARGET SRB2SDL2
+	POST_BUILD
+	COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${CMAKE_CURRENT_SOURCE_DIR}/comptime.c
+)
 
-if("${CMAKE_COMPILER_IS_GNUCC}" AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows" AND NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}" AND NOT "${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}")
-	# On MinGW with internal libraries, link the standard library statically
-	target_link_options(SRB2SDL2 PRIVATE "-static")
+# config.h is generated by this command. It should be done at
+# build time for accurate git information and before anything
+# that needs it, obviously.
+add_custom_target(_SRB2_reconf ALL
+	COMMAND ${CMAKE_COMMAND} -DGIT_EXECUTABLE=${GIT_EXECUTABLE} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DBINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}/.. -P ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/Comptime.cmake
+	WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.."
+)
+add_dependencies(SRB2SDL2 _SRB2_reconf)
+
+if("${CMAKE_COMPILER_IS_GNUCC}" AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows")
+	target_link_options(SRB2SDL2 PRIVATE "-Wl,--disable-dynamicbase")
+	if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}" AND NOT "${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}")
+		# On MinGW with internal libraries, link the standard library statically
+		target_link_options(SRB2SDL2 PRIVATE "-static")
+	endif()
 endif()
 
 target_compile_features(SRB2SDL2 PRIVATE c_std_11 cxx_std_17)
 
-# Core sources
-target_sourcefile(c)
-target_sources(SRB2SDL2 PRIVATE comptime.c md5.c config.h.in)
-
 ### Configuration
 set(SRB2_CONFIG_DEV_BUILD OFF CACHE BOOL
 	"Compile a development build of SRB2.")
diff --git a/src/Makefile b/src/Makefile
index 92f4c0c66c735e9930ac85cc8f26ee87c70658b6..e1d9cb384f15bf6943244678001d12d1305bf310 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -391,3 +391,5 @@ ifdef WINDOWSHELL
 else
 	@:
 endif
+
+$(warning The handwritten GNU Makefile for SRB2 is deprecated, and may be removed in the future. Please consider switching to CMake.)
diff --git a/src/blua/CMakeLists.txt b/src/blua/CMakeLists.txt
index 4e9c67d2f348a8bfed899e4002d25136284b031f..892bf534addc810434bdd00960ebe7a92ef7bde4 100644
--- a/src/blua/CMakeLists.txt
+++ b/src/blua/CMakeLists.txt
@@ -1 +1,28 @@
-target_sourcefile(c)
+target_sources(SRB2SDL2 PRIVATE
+	lapi.c
+	lbaselib.c
+	ldo.c
+	lfunc.c
+	linit.c
+	liolib.c
+	llex.c
+	lmem.c
+	lobject.c
+	lstate.c
+	lstrlib.c
+	ltablib.c
+	lundump.c
+	lzio.c
+	lauxlib.c
+	lcode.c
+	ldebug.c
+	ldump.c
+	lgc.c
+	lopcodes.c
+	lparser.c
+	lstring.c
+	ltable.c
+	ltm.c
+	lvm.c
+	loslib.c
+)
diff --git a/src/comptime.c b/src/comptime.c
index 398eda0743706cecb4a22d1996f8949564f1fe07..386b53f46904df87fc5f64749cc11db6e6821e3f 100644
--- a/src/comptime.c
+++ b/src/comptime.c
@@ -11,6 +11,9 @@
 #include "config.h"
 const char *compbranch = SRB2_COMP_BRANCH;
 const char *comprevision = SRB2_COMP_REVISION;
+const char *compnote = SRB2_COMP_NOTE;
+const char *comptype = CMAKE_BUILD_TYPE;
+const int compoptimized = SRB2_COMP_OPTIMIZED;
 
 #elif (defined(COMPVERSION))
 #include "comptime.h"
@@ -21,5 +24,12 @@ const char *comprevision = "illegal";
 
 #endif
 
+const int compuncommitted =
+#if (defined(COMPVERSION_UNCOMMITTED))
+1;
+#else
+0;
+#endif
+
 const char *compdate = __DATE__;
 const char *comptime = __TIME__;
diff --git a/src/config.h.in b/src/config.h.in
index 3d6d98375693b220329ebd7b8fe1619def9fe792..0ca0f26e905a0fdddf3741bb9d878517751d5bc9 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -11,8 +11,18 @@
 
 #ifdef CMAKECONFIG
 
-#define SRB2_COMP_REVISION    "${SRB2_COMP_REVISION}"
-#define SRB2_COMP_BRANCH      "${SRB2_COMP_BRANCH}"
+#define SRB2_COMP_REVISION       "${SRB2_COMP_REVISION}"
+#define SRB2_COMP_BRANCH         "${SRB2_COMP_BRANCH}"
+#define SRB2_COMP_NOTE           "${SRB2_COMP_NOTE}"
+// This is done with configure_file instead of defines in order to avoid
+// recompiling the whole target whenever the working directory state changes
+#cmakedefine SRB2_COMP_UNCOMMITTED
+#ifdef SRB2_COMP_UNCOMMITTED
+#define COMPVERSION_UNCOMMITTED
+#endif
+
+#define CMAKE_BUILD_TYPE         "${CMAKE_BUILD_TYPE}"
+#cmakedefine01 SRB2_COMP_OPTIMIZED
 
 #endif
 
diff --git a/src/d_main.c b/src/d_main.c
index b7b7f6616db7d89372eeeb03e6d23b5a6e87a684..22a6762554663a182f473efc2c7354ef824feee6 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -71,6 +71,7 @@
 #include "g_input.h" // tutorial mode control scheming
 #include "m_perfstats.h"
 #include "m_random.h"
+#include "command.h"
 
 #ifdef CMAKECONFIG
 #include "config.h"
@@ -1216,6 +1217,15 @@ D_ConvertVersionNumbers (void)
 #endif
 }
 
+static void Command_assert(void)
+{
+#if !defined(NDEBUG) || defined(PARANOIA)
+	CONS_Printf("Yes, assertions are enabled.\n");
+#else
+	CONS_Printf("No, assertions are NOT enabled.\n");
+#endif
+}
+
 //
 // D_SRB2Main
 //
@@ -1229,6 +1239,11 @@ void D_SRB2Main(void)
 	/* break the version string into version numbers, for netplay */
 	D_ConvertVersionNumbers();
 
+	if (!strcmp(compbranch, ""))
+	{
+		compbranch = "detached HEAD";
+	}
+
 	// Print GPL notice for our console users (Linux)
 	CONS_Printf(
 	"\n\nSonic Robo Blast 2\n"
@@ -1365,6 +1380,8 @@ void D_SRB2Main(void)
 	// Do this up here so that WADs loaded through the command line can use ExecCfg
 	COM_Init();
 
+	COM_AddCommand("assert", Command_assert, COM_LUA);
+
 	// Add any files specified on the command line with
 	// "-file <file>" or "-folder <folder>" to the add-on list
 	if (!((M_GetUrlProtocolArg() || M_CheckParm("-connect")) && !M_CheckParm("-server")))
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 1b987bcf130496249a0bd670765ea981d52d8ed0..b243ab8492860d291f7e7d60d0fbca300c40cb1d 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -3859,7 +3859,7 @@ static void Command_ListWADS_f(void)
 static void Command_Version_f(void)
 {
 #ifdef DEVELOP
-	CONS_Printf("Sonic Robo Blast 2 %s-%s (%s %s) ", compbranch, comprevision, compdate, comptime);
+	CONS_Printf("Sonic Robo Blast 2 %s %s %s (%s %s) ", compbranch, comprevision, compnote, compdate, comptime);
 #else
 	CONS_Printf("Sonic Robo Blast 2 %s (%s %s %s %s) ", VERSIONSTRING, compdate, comptime, comprevision, compbranch);
 #endif
diff --git a/src/d_think.h b/src/d_think.h
index bdb5db3f54135545d27b0018943f0c995fffdcd4..efc1589bf62e277a67ab309f05d33d66af741865 100644
--- a/src/d_think.h
+++ b/src/d_think.h
@@ -17,6 +17,8 @@
 #ifndef __D_THINK__
 #define __D_THINK__
 
+#include "doomdef.h"
+
 #ifdef __GNUG__
 #pragma interface
 #endif
@@ -49,6 +51,11 @@ typedef struct thinker_s
 	// killough 11/98: count of how many other objects reference
 	// this one using pointers. Used for garbage collection.
 	INT32 references;
+
+#ifdef PARANOIA
+	INT32 debug_mobjtype;
+	tic_t debug_time;
+#endif
 } thinker_t;
 
 #endif
diff --git a/src/doomdef.h b/src/doomdef.h
index 84404d6edb62d82633b3f2643b9133a14d6b7ed3..f82b0a4cd4b2317069649ad2bae48626930e2a24 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -108,6 +108,14 @@ FILE *fopenfile(const char*, const char*);
 
 //#define NOMD5
 
+// If you don't disable ALL debug first, you get ALL debug enabled
+#if !defined (NDEBUG)
+#define PACKETDROP
+#define PARANOIA
+#define RANGECHECK
+#define ZDEBUG
+#endif
+
 // Uncheck this to compile debugging code
 //#define RANGECHECK
 //#ifndef PARANOIA
@@ -637,7 +645,16 @@ UINT32 quickncasehash (const char *p, size_t n)
 #define PUNCTUATION "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
 
 // Compile date and time and revision.
-extern const char *compdate, *comptime, *comprevision, *compbranch;
+extern const char
+	*compdate,
+	*comptime,
+	*comprevision,
+	*compbranch,
+	*compnote,
+	*comptype;
+extern int
+	compuncommitted,
+	compoptimized;
 
 // Disabled code and code under testing
 // None of these that are disabled in the normal build are guaranteed to work perfectly
diff --git a/src/g_game.c b/src/g_game.c
index b8c43499850cd49ca39fd6498b37a4d91b95c8ed..de2e33ff71386a0c34bef895ca59c59004231567 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -4266,7 +4266,7 @@ static void G_DoContinued(void)
 {
 	player_t *pl = &players[consoleplayer];
 	I_Assert(!netgame && !multiplayer);
-	I_Assert(pl->continues > 0);
+	//I_Assert(pl->continues > 0);
 
 	if (pl->continues)
 		pl->continues--;
diff --git a/src/hardware/CMakeLists.txt b/src/hardware/CMakeLists.txt
index 4e9c67d2f348a8bfed899e4002d25136284b031f..e7819aba97e2065d36f6f920d4725d7b294505f3 100644
--- a/src/hardware/CMakeLists.txt
+++ b/src/hardware/CMakeLists.txt
@@ -1 +1,14 @@
-target_sourcefile(c)
+target_sources(SRB2SDL2 PRIVATE
+	hw_bsp.c
+	hw_draw.c
+	hw_light.c
+	hw_main.c
+	hw_clip.c
+	hw_md2.c
+	hw_cache.c
+	hw_md2load.c
+	hw_md3load.c
+	hw_model.c
+	hw_batching.c
+	r_opengl/r_opengl.c
+)
diff --git a/src/http-mserv.c b/src/http-mserv.c
index b7032e89a3d65ecec15f195d1d46334435a411d9..df9a71a5c5d6cfcb8f73ecd3eeff371170e76c8e 100644
--- a/src/http-mserv.c
+++ b/src/http-mserv.c
@@ -159,7 +159,7 @@ HMS_connect (const char *format, ...)
 		return NULL;
 	}
 
-	if (cv_masterserver_token.string[0])
+	if (cv_masterserver_token.string && cv_masterserver_token.string[0])
 	{
 		quack_token = curl_easy_escape(curl, cv_masterserver_token.string, 0);
 		token_length = ( sizeof "?token="-1 )+ strlen(quack_token);
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index 0f40b862191b62181525f937961fcd502879ce58..3764acf6a40945489506ce8d94b1b976fa771752 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -508,8 +508,6 @@ static int pivotlist_get(lua_State *L)
 	const char *field = luaL_checkstring(L, 2);
 	UINT8 frame;
 
-	I_Assert(framepivot != NULL);
-
 	frame = R_Char2Frame(field[0]);
 	if (frame == 255)
 		luaL_error(L, "invalid frame %s", field);
@@ -539,8 +537,6 @@ static int pivotlist_set(lua_State *L)
 	if (hook_cmd_running)
 		return luaL_error(L, "Do not alter spriteframepivot_t in CMD building code!");
 
-	I_Assert(pivotlist != NULL);
-
 	frame = R_Char2Frame(field[0]);
 	if (frame == 255)
 		luaL_error(L, "invalid frame %s", field);
diff --git a/src/m_cond.c b/src/m_cond.c
index b21778c697042c4b96fed6126447df803121b077..13a483ea227d0271ee8a70aa0b852f0e53aea101 100644
--- a/src/m_cond.c
+++ b/src/m_cond.c
@@ -113,7 +113,7 @@ void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1
 	condition_t *cond;
 	UINT32 num, wnum;
 
-	I_Assert(set && set <= MAXCONDITIONSETS);
+	I_Assert(set < MAXCONDITIONSETS);
 
 	wnum = conditionSets[set - 1].numconditions;
 	num = ++conditionSets[set - 1].numconditions;
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 4ca59285f7fc46bbfeb6e2373b50eb4eae33637d..8c0fa17ad9dece3d5518412c134955be8285ed80 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -11181,12 +11181,6 @@ void P_RemoveMobj(mobj_t *mobj)
 
 	P_SetTarget(&mobj->hnext, P_SetTarget(&mobj->hprev, NULL));
 
-	// DBG: set everything in mobj_t to 0xFF instead of leaving it. debug memory error.
-#ifdef SCRAMBLE_REMOVED
-	// Invalidate mobj_t data to cause crashes if accessed!
-	memset((UINT8 *)mobj + sizeof(thinker_t), 0xff, sizeof(mobj_t) - sizeof(thinker_t));
-#endif
-
 	R_RemoveMobjInterpolator(mobj);
 
 	// free block
@@ -11205,6 +11199,17 @@ void P_RemoveMobj(mobj_t *mobj)
 	}
 
 	P_RemoveThinker((thinker_t *)mobj);
+
+#ifdef PARANOIA
+	// Saved to avoid being scrambled like below...
+	mobj->thinker.debug_mobjtype = mobj->type;
+#endif
+
+	// DBG: set everything in mobj_t to 0xFF instead of leaving it. debug memory error.
+#ifdef SCRAMBLE_REMOVED
+	// Invalidate mobj_t data to cause crashes if accessed!
+	memset((UINT8 *)mobj + sizeof(thinker_t), 0xff, sizeof(mobj_t) - sizeof(thinker_t));
+#endif
 }
 
 // This does not need to be added to Lua.
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 8f11e63e58a25ba43ff6f78f3d9713332330e402..faecd13770b3d81b992017af51b4b663487aa50d 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -2716,8 +2716,8 @@ static void P_NetArchiveThinkers(void)
 				continue;
 			}
 #ifdef PARANOIA
-			else if (th->function.acp1 != (actionf_p1)P_RemoveThinkerDelayed) // wait garbage collection
-				I_Error("unknown thinker type %p", th->function.acp1);
+			else
+				I_Assert(th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed); // wait garbage collection
 #endif
 		}
 
diff --git a/src/p_tick.c b/src/p_tick.c
index b1fd367ed94721e5aedab16b3a3e743a6df63425..ec5d8a2da0a487a6e4743e39dedc04c2fb27069d 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -30,6 +30,10 @@
 // Object place
 #include "m_cheat.h"
 
+#ifdef PARANOIA
+#include "deh_tables.h" // MOBJTYPE_LIST
+#endif
+
 tic_t leveltime;
 
 //
@@ -211,7 +215,48 @@ void P_AddThinker(const thinklistnum_t n, thinker_t *thinker)
 	thlist[n].prev = thinker;
 
 	thinker->references = 0;    // killough 11/98: init reference counter to 0
+
+#ifdef PARANOIA
+	thinker->debug_mobjtype = MT_NULL;
+#endif
+}
+
+#ifdef PARANOIA
+static const char *MobjTypeName(const mobj_t *mobj)
+{
+	actionf_p1 p1 = mobj->thinker.function.acp1;
+
+	if (p1 == (actionf_p1)P_MobjThinker)
+	{
+		return MOBJTYPE_LIST[mobj->type];
+	}
+	else if (p1 == (actionf_p1)P_RemoveThinkerDelayed)
+	{
+		if (mobj->thinker.debug_mobjtype != MT_NULL)
+		{
+			return MOBJTYPE_LIST[mobj->thinker.debug_mobjtype];
+		}
+	}
+
+	return "<Not a mobj>";
+}
+
+static const char *MobjThinkerName(const mobj_t *mobj)
+{
+	actionf_p1 p1 = mobj->thinker.function.acp1;
+
+	if (p1 == (actionf_p1)P_MobjThinker)
+	{
+		return "P_MobjThinker";
+	}
+	else if (p1 == (actionf_p1)P_RemoveThinkerDelayed)
+	{
+		return "P_RemoveThinkerDelayed";
+	}
+
+	return "<Unknown Thinker>";
 }
+#endif
 
 //
 // killough 11/98:
@@ -234,20 +279,34 @@ static thinker_t *currentthinker;
 void P_RemoveThinkerDelayed(thinker_t *thinker)
 {
 	thinker_t *next;
-#ifdef PARANOIA
-#define BEENAROUNDBIT (0x40000000) // has to be sufficiently high that it's unlikely to happen in regular gameplay. If you change this, pay attention to the bit pattern of INT32_MIN.
-	if (thinker->references & ~BEENAROUNDBIT)
+
+	if (thinker->references != 0)
 	{
-		if (thinker->references & BEENAROUNDBIT) // Usually gets cleared up in one frame; what's going on here, then?
-			CONS_Printf("Number of potentially faulty references: %d\n", (thinker->references & ~BEENAROUNDBIT));
-		thinker->references |= BEENAROUNDBIT;
+#ifdef PARANOIA
+		if (thinker->debug_time > leveltime)
+		{
+			thinker->debug_time = leveltime + 2; // do not print errors again
+		}
+		// Removed mobjs can be the target of another mobj. In
+		// that case, the other mobj will manage its reference
+		// to the removed mobj in P_MobjThinker. However, if
+		// the removed mobj is removed after the other object
+		// thinks, the reference management is delayed by one
+		// tic.
+		else if (thinker->debug_time < leveltime)
+		{
+			CONS_Printf(
+					"PARANOIA/P_RemoveThinkerDelayed: %p %s references=%d\n",
+					(void*)thinker,
+					MobjTypeName((mobj_t*)thinker),
+					thinker->references
+			);
+
+			thinker->debug_time = leveltime + 2; // do not print this error again
+		}
+#endif
 		return;
 	}
-#undef BEENAROUNDBIT
-#else
-	if (thinker->references)
-		return;
-#endif
 
 	/* Remove from main thinker list */
 	next = thinker->next;
@@ -291,12 +350,45 @@ void P_RemoveThinker(thinker_t *thinker)
  * references, and delay removal until the count is 0.
  */
 
-mobj_t *P_SetTarget(mobj_t **mop, mobj_t *targ)
+mobj_t *P_SetTarget2(mobj_t **mop, mobj_t *targ
+#ifdef PARANOIA
+		, const char *source_file, int source_line
+#endif
+)
 {
-	if (*mop)              // If there was a target already, decrease its refcount
+	if (*mop) // If there was a target already, decrease its refcount
+	{
 		(*mop)->thinker.references--;
-if ((*mop = targ) != NULL) // Set new target and if non-NULL, increase its counter
+
+#ifdef PARANOIA
+		if ((*mop)->thinker.references < 0)
+		{
+			CONS_Printf(
+					"PARANOIA/P_SetTarget: %p %s %s references=%d, references go negative! (%s:%d)\n",
+					(void*)*mop,
+					MobjTypeName(*mop),
+					MobjThinkerName(*mop),
+					(*mop)->thinker.references,
+					source_file,
+					source_line
+			);
+		}
+
+		(*mop)->thinker.debug_time = leveltime;
+#endif
+	}
+
+	if (targ != NULL) // Set new target and if non-NULL, increase its counter
+	{
 		targ->thinker.references++;
+
+#ifdef PARANOIA
+		targ->thinker.debug_time = leveltime;
+#endif
+	}
+
+	*mop = targ;
+
 	return targ;
 }
 
diff --git a/src/p_tick.h b/src/p_tick.h
index 594bbc7afb608ce88a54b8f63ae7e5494f96af4f..bbc227e081433652a9a3d79b8ab0513c531677bc 100644
--- a/src/p_tick.h
+++ b/src/p_tick.h
@@ -14,6 +14,8 @@
 #ifndef __P_TICK__
 #define __P_TICK__
 
+#include "doomdef.h"
+
 #ifdef __GNUG__
 #pragma interface
 #endif
@@ -28,6 +30,17 @@ void P_Ticker(boolean run);
 void P_PreTicker(INT32 frames);
 void P_DoTeamscrambling(void);
 void P_RemoveThinkerDelayed(thinker_t *thinker); //killed
-mobj_t *P_SetTarget(mobj_t **mo, mobj_t *target);   // killough 11/98
+
+mobj_t *P_SetTarget2(mobj_t **mo, mobj_t *target
+#ifdef PARANOIA
+		, const char *source_file, int source_line
+#endif
+);
+
+#ifdef PARANOIA
+#define P_SetTarget(...) P_SetTarget2(__VA_ARGS__, __FILE__, __LINE__)
+#else
+#define P_SetTarget P_SetTarget2
+#endif
 
 #endif
diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt
index aab83ca2841efe125ca1776d8ceb62bd7282810f..4c4cdafb640e7806ee1efb79380ee10eaf26e119 100644
--- a/src/sdl/CMakeLists.txt
+++ b/src/sdl/CMakeLists.txt
@@ -1,12 +1,17 @@
 # Declare SDL2 interface sources
 
-target_sources(SRB2SDL2 PRIVATE mixer_sound.c)
-
-target_sourcefile(c)
-
-target_sources(SRB2SDL2 PRIVATE ogl_sdl.c)
-
-target_sources(SRB2SDL2 PRIVATE i_threads.c)
+target_sources(SRB2SDL2 PRIVATE
+	mixer_sound.c
+	ogl_sdl.c
+	i_threads.c
+	i_net.c
+	i_system.c
+	i_main.c
+	i_video.c
+	dosstr.c
+	endtxt.c
+	hwsym_sdl.c
+)
 
 if("${CMAKE_SYSTEM_NAME}" MATCHES Windows)
 	target_sources(SRB2SDL2 PRIVATE
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index c21226ac3fba998ed0590f3efcebb0ee3482f8be..075199a19ac105242bfc8d6306a3c79580466533 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -23,12 +23,6 @@
 /// \file
 /// \brief SRB2 system stuff for SDL
 
-#ifdef CMAKECONFIG
-#include "config.h"
-#else
-#include "../config.h.in"
-#endif
-
 #include <signal.h>
 
 #ifdef _WIN32
@@ -2362,7 +2356,10 @@ INT32 I_StartupSystem(void)
 #endif
 	I_StartupConsole();
 #ifdef NEWSIGNALHANDLER
-	I_Fork();
+	// This is useful when debugging. It lets GDB attach to
+	// the correct process easily.
+	if (!M_CheckParm("-nofork"))
+		I_Fork();
 #endif
 	I_RegisterSignals();
 	I_OutputMsg("Compiled for SDL version: %d.%d.%d\n",
@@ -2648,9 +2645,10 @@ void I_ShutdownSystem(void)
 {
 	INT32 c;
 
-#ifndef NEWSIGNALHANDLER
-	I_ShutdownConsole();
+#ifdef NEWSIGNALHANDLER
+	if (M_CheckParm("-nofork"))
 #endif
+		I_ShutdownConsole();
 
 	for (c = MAX_QUIT_FUNCS-1; c >= 0; c--)
 		if (quit_funcs[c])
diff --git a/src/z_zone.h b/src/z_zone.h
index f00f57749407c053dabc119a81c5b9c4a8ed07e7..ce7af4a159555e3a6c2be83e8d0eadcf8a7a69eb 100644
--- a/src/z_zone.h
+++ b/src/z_zone.h
@@ -15,6 +15,7 @@
 #define __Z_ZONE__
 
 #include <stdio.h>
+#include "doomdef.h"
 #include "doomtype.h"
 
 #ifdef __GNUC__ // __attribute__ ((X))
diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt
index 7aff16601efd49d71693a137a4aab7b98721e7fa..f33b3bf3f836b86b98fda8e78f73ec5be19758d8 100644
--- a/thirdparty/CMakeLists.txt
+++ b/thirdparty/CMakeLists.txt
@@ -9,521 +9,13 @@ else()
 	set(NOT_SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES ON)
 endif()
 
-
-if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}")
-	CPMAddPackage(
-		NAME SDL2
-		VERSION 2.24.2
-		URL "https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.24.2.zip"
-		EXCLUDE_FROM_ALL ON
-		OPTIONS
-			"BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
-			"SDL_SHARED ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
-			"SDL_STATIC ${NOT_SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
-			"SDL_TEST OFF"
-			"SDL2_DISABLE_SDL2MAIN ON"
-			"SDL2_DISABLE_INSTALL ON"
-	)
-endif()
-
-if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}")
-	CPMAddPackage(
-		NAME SDL2_mixer
-		VERSION 2.6.2
-		URL "https://github.com/libsdl-org/SDL_mixer/archive/refs/tags/release-2.6.2.zip"
-		EXCLUDE_FROM_ALL ON
-		OPTIONS
-			"BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
-			"SDL2MIXER_INSTALL OFF"
-			"SDL2MIXER_DEPS_SHARED OFF"
-			"SDL2MIXER_SAMPLES OFF"
-			"SDL2MIXER_VENDORED ON"
-			"SDL2MIXER_FLAC ON"
-			"SDL2MIXER_FLAC_LIBFLAC OFF"
-			"SDL2MIXER_FLAC_DRFLAC ON"
-			"SDL2MIXER_MOD OFF"
-			"SDL2MIXER_MP3 ON"
-			"SDL2MIXER_MP3_DRMP3 ON"
-			"SDL2MIXER_MIDI ON"
-			"SDL2MIXER_OPUS OFF"
-			"SDL2MIXER_VORBIS STB"
-			"SDL2MIXER_WAVE ON"
-	)
-endif()
-
-if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}")
-	CPMAddPackage(
-		NAME ZLIB
-		VERSION 1.2.13
-		URL "https://github.com/madler/zlib/archive/refs/tags/v1.2.13.zip"
-		EXCLUDE_FROM_ALL
-		DOWNLOAD_ONLY YES
-	)
-	if(ZLIB_ADDED)
-		set(ZLIB_SRCS
-			crc32.h
-			deflate.h
-			gzguts.h
-			inffast.h
-			inffixed.h
-			inflate.h
-			inftrees.h
-			trees.h
-			zutil.h
-
-			adler32.c
-			compress.c
-			crc32.c
-			deflate.c
-			gzclose.c
-			gzlib.c
-			gzread.c
-			gzwrite.c
-			inflate.c
-			infback.c
-			inftrees.c
-			inffast.c
-			trees.c
-			uncompr.c
-			zutil.c
-		)
-		list(TRANSFORM ZLIB_SRCS PREPEND "${ZLIB_SOURCE_DIR}/")
-
-		configure_file("${ZLIB_SOURCE_DIR}/zlib.pc.cmakein" "${ZLIB_BINARY_DIR}/zlib.pc" @ONLY)
-		configure_file("${ZLIB_SOURCE_DIR}/zconf.h.cmakein" "${ZLIB_BINARY_DIR}/include/zconf.h" @ONLY)
-		configure_file("${ZLIB_SOURCE_DIR}/zlib.h" "${ZLIB_BINARY_DIR}/include/zlib.h" @ONLY)
-
-		add_library(ZLIB ${SRB2_INTERNAL_LIBRARY_TYPE} ${ZLIB_SRCS})
-		set_target_properties(ZLIB PROPERTIES
-			VERSION 1.2.13
-			OUTPUT_NAME "z"
-		)
-		target_include_directories(ZLIB PRIVATE "${ZLIB_SOURCE_DIR}")
-		target_include_directories(ZLIB PUBLIC "${ZLIB_BINARY_DIR}/include")
-		if(MSVC)
-			target_compile_definitions(ZLIB PRIVATE -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE)
-		endif()
-		add_library(ZLIB::ZLIB ALIAS ZLIB)
-	endif()
-endif()
-
 if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}")
-	CPMAddPackage(
-		NAME png
-		VERSION 1.6.38
-		URL "https://github.com/glennrp/libpng/archive/refs/tags/v1.6.38.zip"
-		# png cmake build is broken on msys/mingw32
-		DOWNLOAD_ONLY YES
-	)
-
-	if(png_ADDED)
-		# Since png's cmake build is broken, we're going to create a target manually
-		set(
-			PNG_SOURCES
-
-			png.h
-			pngconf.h
-
-			pngpriv.h
-			pngdebug.h
-			pnginfo.h
-			pngstruct.h
-
-			png.c
-			pngerror.c
-			pngget.c
-			pngmem.c
-			pngpread.c
-			pngread.c
-			pngrio.c
-			pngrtran.c
-			pngrutil.c
-			pngset.c
-			pngtrans.c
-			pngwio.c
-			pngwrite.c
-			pngwtran.c
-			pngwutil.c
-		)
-		list(TRANSFORM PNG_SOURCES PREPEND "${png_SOURCE_DIR}/")
-
-		add_custom_command(
-			OUTPUT "${png_BINARY_DIR}/include/png.h" "${png_BINARY_DIR}/include/pngconf.h"
-			COMMAND ${CMAKE_COMMAND} -E copy "${png_SOURCE_DIR}/png.h" "${png_SOURCE_DIR}/pngconf.h" "${png_BINARY_DIR}/include"
-			DEPENDS "${png_SOURCE_DIR}/png.h" "${png_SOURCE_DIR}/pngconf.h"
-			VERBATIM
-		)
-		add_custom_command(
-			OUTPUT "${png_BINARY_DIR}/include/pnglibconf.h"
-			COMMAND ${CMAKE_COMMAND} -E copy "${png_SOURCE_DIR}/scripts/pnglibconf.h.prebuilt" "${png_BINARY_DIR}/include/pnglibconf.h"
-			DEPENDS "${png_SOURCE_DIR}/scripts/pnglibconf.h.prebuilt"
-			VERBATIM
-		)
-		list(
-			APPEND PNG_SOURCES
-			"${png_BINARY_DIR}/include/png.h"
-			"${png_BINARY_DIR}/include/pngconf.h"
-			"${png_BINARY_DIR}/include/pnglibconf.h"
-		)
-		add_library(png "${SRB2_INTERNAL_LIBRARY_TYPE}" ${PNG_SOURCES})
-
-		# Disable ARM NEON since having it automatic breaks libpng external build on clang for some reason
-		target_compile_definitions(png PRIVATE -DPNG_ARM_NEON_OPT=0)
-
-		# The png includes need to be available to consumers
-		target_include_directories(png PUBLIC "${png_BINARY_DIR}/include")
-
-		# ... and these also need to be present only for png build
-		target_include_directories(png PRIVATE "${ZLIB_SOURCE_DIR}")
-		target_include_directories(png PRIVATE "${ZLIB_BINARY_DIR}")
-		target_include_directories(png PRIVATE "${png_BINARY_DIR}")
-
-		target_link_libraries(png PRIVATE ZLIB::ZLIB)
-		add_library(PNG::PNG ALIAS png)
-	endif()
+	include("cpm-sdl2.cmake")
+	include("cpm-sdl2-mixer.cmake")
+	include("cpm-zlib.cmake")
+	include("cpm-png.cmake")
+	include("cpm-curl.cmake")
+	include("cpm-openmpt.cmake")
 endif()
 
-if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}")
-	set(
-		internal_curl_options
-
-		"BUILD_CURL_EXE OFF"
-		"BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
-		"CURL_DISABLE_TESTS ON"
-		"HTTP_ONLY ON"
-		"CURL_DISABLE_CRYPTO_AUTH ON"
-		"CURL_DISABLE_NTLM ON"
-		"ENABLE_MANUAL OFF"
-		"ENABLE_THREADED_RESOLVER OFF"
-		"CURL_USE_LIBPSL OFF"
-		"CURL_USE_LIBSSH2 OFF"
-		"USE_LIBIDN2 OFF"
-		"CURL_ENABLE_EXPORT_TARGET OFF"
-	)
-	if(${CMAKE_SYSTEM} MATCHES Windows)
-		list(APPEND internal_curl_options "CURL_USE_OPENSSL OFF")
-		list(APPEND internal_curl_options "CURL_USE_SCHANNEL ON")
-	endif()
-	if(${CMAKE_SYSTEM} MATCHES Darwin)
-		list(APPEND internal_curl_options "CURL_USE_OPENSSL OFF")
-		list(APPEND internal_curl_options "CURL_USE_SECTRANSP ON")
-	endif()
-	if(${CMAKE_SYSTEM} MATCHES Linux)
-		list(APPEND internal_curl_options "CURL_USE_OPENSSL ON")
-	endif()
-
-	CPMAddPackage(
-		NAME curl
-		VERSION 7.86.0
-		URL "https://github.com/curl/curl/archive/refs/tags/curl-7_86_0.zip"
-		EXCLUDE_FROM_ALL ON
-		OPTIONS ${internal_curl_options}
-	)
-endif()
-
-if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}")
-	CPMAddPackage(
-		NAME openmpt
-		VERSION 0.4.30
-		URL "https://github.com/OpenMPT/openmpt/archive/refs/tags/libopenmpt-0.4.30.zip"
-		DOWNLOAD_ONLY ON
-	)
-
-	if(openmpt_ADDED)
-		set(
-			openmpt_SOURCES
-
-			# minimp3
-			# -DMPT_WITH_MINIMP3
-			include/minimp3/minimp3.c
-
-			common/mptStringParse.cpp
-			common/mptLibrary.cpp
-			common/Logging.cpp
-			common/Profiler.cpp
-			common/version.cpp
-			common/mptCPU.cpp
-			common/ComponentManager.cpp
-			common/mptOS.cpp
-			common/serialization_utils.cpp
-			common/mptStringFormat.cpp
-			common/FileReader.cpp
-			common/mptWine.cpp
-			common/mptPathString.cpp
-			common/mptAlloc.cpp
-			common/mptUUID.cpp
-			common/mptTime.cpp
-			common/mptString.cpp
-			common/mptFileIO.cpp
-			common/mptStringBuffer.cpp
-			common/mptRandom.cpp
-			common/mptIO.cpp
-			common/misc_util.cpp
-
-			common/mptCRC.h
-			common/mptLibrary.h
-			common/mptIO.h
-			common/version.h
-			common/stdafx.h
-			common/ComponentManager.h
-			common/Endianness.h
-			common/mptStringFormat.h
-			common/mptMutex.h
-			common/mptUUID.h
-			common/mptExceptionText.h
-			common/BuildSettings.h
-			common/mptAlloc.h
-			common/mptTime.h
-			common/FileReaderFwd.h
-			common/Logging.h
-			common/mptException.h
-			common/mptWine.h
-			common/mptStringBuffer.h
-			common/misc_util.h
-			common/mptBaseMacros.h
-			common/mptMemory.h
-			common/mptFileIO.h
-			common/serialization_utils.h
-			common/mptSpan.h
-			common/mptThread.h
-			common/FlagSet.h
-			common/mptString.h
-			common/mptStringParse.h
-			common/mptBaseUtils.h
-			common/mptRandom.h
-			common/CompilerDetect.h
-			common/FileReader.h
-			common/mptAssert.h
-			common/mptPathString.h
-			common/Profiler.h
-			common/mptOS.h
-			common/mptBaseTypes.h
-			common/mptCPU.h
-			common/mptBufferIO.h
-			common/versionNumber.h
-
-			soundlib/WAVTools.cpp
-			soundlib/ITTools.cpp
-			soundlib/AudioCriticalSection.cpp
-			soundlib/Load_stm.cpp
-			soundlib/MixerLoops.cpp
-			soundlib/Load_dbm.cpp
-			soundlib/ModChannel.cpp
-			soundlib/Load_gdm.cpp
-			soundlib/Snd_fx.cpp
-			soundlib/Load_mid.cpp
-			soundlib/mod_specifications.cpp
-			soundlib/Snd_flt.cpp
-			soundlib/Load_psm.cpp
-			soundlib/Load_far.cpp
-			soundlib/patternContainer.cpp
-			soundlib/Load_med.cpp
-			soundlib/Load_dmf.cpp
-			soundlib/Paula.cpp
-			soundlib/modcommand.cpp
-			soundlib/Message.cpp
-			soundlib/SoundFilePlayConfig.cpp
-			soundlib/Load_uax.cpp
-			soundlib/plugins/PlugInterface.cpp
-			soundlib/plugins/LFOPlugin.cpp
-			soundlib/plugins/PluginManager.cpp
-			soundlib/plugins/DigiBoosterEcho.cpp
-			soundlib/plugins/dmo/DMOPlugin.cpp
-			soundlib/plugins/dmo/Flanger.cpp
-			soundlib/plugins/dmo/Distortion.cpp
-			soundlib/plugins/dmo/ParamEq.cpp
-			soundlib/plugins/dmo/Gargle.cpp
-			soundlib/plugins/dmo/I3DL2Reverb.cpp
-			soundlib/plugins/dmo/Compressor.cpp
-			soundlib/plugins/dmo/WavesReverb.cpp
-			soundlib/plugins/dmo/Echo.cpp
-			soundlib/plugins/dmo/Chorus.cpp
-			soundlib/Load_ams.cpp
-			soundlib/tuningbase.cpp
-			soundlib/ContainerUMX.cpp
-			soundlib/Load_ptm.cpp
-			soundlib/ContainerXPK.cpp
-			soundlib/SampleFormatMP3.cpp
-			soundlib/tuning.cpp
-			soundlib/Sndfile.cpp
-			soundlib/ContainerMMCMP.cpp
-			soundlib/Load_amf.cpp
-			soundlib/Load_669.cpp
-			soundlib/modsmp_ctrl.cpp
-			soundlib/Load_mtm.cpp
-			soundlib/OggStream.cpp
-			soundlib/Load_plm.cpp
-			soundlib/Tables.cpp
-			soundlib/Load_c67.cpp
-			soundlib/Load_mod.cpp
-			soundlib/Load_sfx.cpp
-			soundlib/Sndmix.cpp
-			soundlib/load_j2b.cpp
-			soundlib/ModSequence.cpp
-			soundlib/SampleFormatFLAC.cpp
-			soundlib/ModInstrument.cpp
-			soundlib/Load_mo3.cpp
-			soundlib/ModSample.cpp
-			soundlib/Dlsbank.cpp
-			soundlib/Load_itp.cpp
-			soundlib/UpgradeModule.cpp
-			soundlib/MIDIMacros.cpp
-			soundlib/ContainerPP20.cpp
-			soundlib/RowVisitor.cpp
-			soundlib/Load_imf.cpp
-			soundlib/SampleFormatVorbis.cpp
-			soundlib/Load_dsm.cpp
-			soundlib/Load_mt2.cpp
-			soundlib/MixerSettings.cpp
-			soundlib/S3MTools.cpp
-			soundlib/Load_xm.cpp
-			soundlib/MIDIEvents.cpp
-			soundlib/pattern.cpp
-			soundlib/Load_digi.cpp
-			soundlib/Load_s3m.cpp
-			soundlib/tuningCollection.cpp
-			soundlib/SampleIO.cpp
-			soundlib/Dither.cpp
-			soundlib/Load_mdl.cpp
-			soundlib/OPL.cpp
-			soundlib/WindowedFIR.cpp
-			soundlib/SampleFormats.cpp
-			soundlib/Load_wav.cpp
-			soundlib/Load_it.cpp
-			soundlib/UMXTools.cpp
-			soundlib/Load_stp.cpp
-			soundlib/Load_okt.cpp
-			soundlib/Load_ult.cpp
-			soundlib/MixFuncTable.cpp
-			soundlib/SampleFormatOpus.cpp
-			soundlib/Fastmix.cpp
-			soundlib/Tagging.cpp
-			soundlib/ITCompression.cpp
-			soundlib/Load_dtm.cpp
-			soundlib/MPEGFrame.cpp
-			soundlib/XMTools.cpp
-			soundlib/SampleFormatMediaFoundation.cpp
-			soundlib/InstrumentExtensions.cpp
-
-			soundlib/MixerInterface.h
-			soundlib/SoundFilePlayConfig.h
-			soundlib/ModSample.h
-			soundlib/MIDIEvents.h
-			soundlib/ModSampleCopy.h
-			soundlib/patternContainer.h
-			soundlib/ChunkReader.h
-			soundlib/ITCompression.h
-			soundlib/Dither.h
-			soundlib/S3MTools.h
-			soundlib/MPEGFrame.h
-			soundlib/WAVTools.h
-			soundlib/mod_specifications.h
-			soundlib/ITTools.h
-			soundlib/RowVisitor.h
-			soundlib/plugins/PluginMixBuffer.h
-			soundlib/plugins/PluginStructs.h
-			soundlib/plugins/LFOPlugin.h
-			soundlib/plugins/PlugInterface.h
-			soundlib/plugins/DigiBoosterEcho.h
-			soundlib/plugins/OpCodes.h
-			soundlib/plugins/dmo/Echo.h
-			soundlib/plugins/dmo/I3DL2Reverb.h
-			soundlib/plugins/dmo/WavesReverb.h
-			soundlib/plugins/dmo/ParamEq.h
-			soundlib/plugins/dmo/Gargle.h
-			soundlib/plugins/dmo/DMOPlugin.h
-			soundlib/plugins/dmo/Chorus.h
-			soundlib/plugins/dmo/Compressor.h
-			soundlib/plugins/dmo/Distortion.h
-			soundlib/plugins/dmo/Flanger.h
-			soundlib/plugins/PluginManager.h
-			soundlib/SampleIO.h
-			soundlib/Container.h
-			soundlib/ModSequence.h
-			soundlib/UMXTools.h
-			soundlib/Message.h
-			soundlib/modcommand.h
-			soundlib/XMTools.h
-			soundlib/Snd_defs.h
-			soundlib/MixFuncTable.h
-			soundlib/pattern.h
-			soundlib/modsmp_ctrl.h
-			soundlib/Tagging.h
-			soundlib/tuningcollection.h
-			soundlib/Mixer.h
-			soundlib/FloatMixer.h
-			soundlib/AudioCriticalSection.h
-			soundlib/Tables.h
-			soundlib/tuningbase.h
-			soundlib/WindowedFIR.h
-			soundlib/Sndfile.h
-			soundlib/Paula.h
-			soundlib/ModInstrument.h
-			soundlib/Dlsbank.h
-			soundlib/IntMixer.h
-			soundlib/OPL.h
-			soundlib/Resampler.h
-			soundlib/ModChannel.h
-			soundlib/MixerSettings.h
-			soundlib/AudioReadTarget.h
-			soundlib/MixerLoops.h
-			soundlib/tuning.h
-			soundlib/MIDIMacros.h
-			soundlib/OggStream.h
-			soundlib/Loaders.h
-			soundlib/BitReader.h
-			soundlib/opal.h
-
-			sounddsp/AGC.cpp
-			sounddsp/EQ.cpp
-			sounddsp/DSP.cpp
-			sounddsp/Reverb.cpp
-			sounddsp/Reverb.h
-			sounddsp/EQ.h
-			sounddsp/DSP.h
-			sounddsp/AGC.h
-
-			libopenmpt/libopenmpt_c.cpp
-			libopenmpt/libopenmpt_cxx.cpp
-			libopenmpt/libopenmpt_impl.cpp
-			libopenmpt/libopenmpt_ext_impl.cpp
-		)
-		list(TRANSFORM openmpt_SOURCES PREPEND "${openmpt_SOURCE_DIR}/")
-
-		# -DLIBOPENMPT_BUILD
-		configure_file("openmpt_svn_version.h" "svn_version.h")
-		add_library(openmpt "${SRB2_INTERNAL_LIBRARY_TYPE}" ${openmpt_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/svn_version.h)
-		if("${CMAKE_C_COMPILER_ID}" STREQUAL GNU OR "${CMAKE_C_COMPILER_ID}" STREQUAL Clang OR "${CMAKE_C_COMPILER_ID}" STREQUAL AppleClang)
-			target_compile_options(openmpt PRIVATE "-g0")
-		endif()
-		if("${CMAKE_SYSTEM_NAME}" STREQUAL Windows AND "${CMAKE_C_COMPILER_ID}" STREQUAL MSVC)
-			target_link_libraries(openmpt PRIVATE Rpcrt4)
-		endif()
-		target_compile_features(openmpt PRIVATE cxx_std_11)
-		target_compile_definitions(openmpt PRIVATE -DLIBOPENMPT_BUILD)
-
-		target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}/common")
-		target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}/src")
-		target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}/include")
-		target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}")
-		target_include_directories(openmpt PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
-
-		# I wish this wasn't necessary, but it is
-		target_include_directories(openmpt PUBLIC "${openmpt_SOURCE_DIR}")
-	endif()
-endif()
-
-if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}")
-	CPMAddPackage(
-		NAME libgme
-		VERSION 0.6.3
-		URL "https://bitbucket.org/mpyne/game-music-emu/get/e76bdc0cb916e79aa540290e6edd0c445879d3ba.zip"
-		EXCLUDE_FROM_ALL ON
-		OPTIONS
-			"BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
-			"ENABLE_UBSAN OFF"
-			"GME_YM2612_EMU MAME"
-	)
-	target_compile_features(gme PRIVATE cxx_std_11)
-	target_link_libraries(gme PRIVATE ZLIB::ZLIB)
-endif()
+include("cpm-libgme.cmake")
diff --git a/thirdparty/cpm-curl.cmake b/thirdparty/cpm-curl.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..3d8c6e61d46dee6f06f4e6d69beb09d1605f6584
--- /dev/null
+++ b/thirdparty/cpm-curl.cmake
@@ -0,0 +1,35 @@
+set(
+	internal_curl_options
+
+	"BUILD_CURL_EXE OFF"
+	"BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
+	"CURL_DISABLE_TESTS ON"
+	"HTTP_ONLY ON"
+	"CURL_DISABLE_CRYPTO_AUTH ON"
+	"CURL_DISABLE_NTLM ON"
+	"ENABLE_MANUAL OFF"
+	"ENABLE_THREADED_RESOLVER OFF"
+	"CURL_USE_LIBPSL OFF"
+	"CURL_USE_LIBSSH2 OFF"
+	"USE_LIBIDN2 OFF"
+	"CURL_ENABLE_EXPORT_TARGET OFF"
+)
+if(${CMAKE_SYSTEM} MATCHES Windows)
+	list(APPEND internal_curl_options "CURL_USE_OPENSSL OFF")
+	list(APPEND internal_curl_options "CURL_USE_SCHANNEL ON")
+endif()
+if(${CMAKE_SYSTEM} MATCHES Darwin)
+	list(APPEND internal_curl_options "CURL_USE_OPENSSL OFF")
+	list(APPEND internal_curl_options "CURL_USE_SECTRANSP ON")
+endif()
+if(${CMAKE_SYSTEM} MATCHES Linux)
+	list(APPEND internal_curl_options "CURL_USE_OPENSSL ON")
+endif()
+
+CPMAddPackage(
+	NAME curl
+	VERSION 7.86.0
+	URL "https://github.com/curl/curl/archive/refs/tags/curl-7_86_0.zip"
+	EXCLUDE_FROM_ALL ON
+	OPTIONS ${internal_curl_options}
+)
diff --git a/thirdparty/cpm-libgme.cmake b/thirdparty/cpm-libgme.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..f15bc3b31cb23ed7663ed950c14c1d6a0dc36567
--- /dev/null
+++ b/thirdparty/cpm-libgme.cmake
@@ -0,0 +1,16 @@
+CPMAddPackage(
+	NAME libgme
+	VERSION 0.6.3
+	URL "https://bitbucket.org/mpyne/game-music-emu/get/e76bdc0cb916e79aa540290e6edd0c445879d3ba.zip"
+	EXCLUDE_FROM_ALL ON
+	OPTIONS
+		"BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
+		"ENABLE_UBSAN OFF"
+		"GME_YM2612_EMU MAME"
+)
+
+if(libgme_ADDED)
+	target_compile_features(gme PRIVATE cxx_std_11)
+	# libgme's CMakeLists.txt already links this
+	#target_link_libraries(gme PRIVATE ZLIB::ZLIB)
+endif()
diff --git a/thirdparty/cpm-openmpt.cmake b/thirdparty/cpm-openmpt.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..01f7ff75f64d915799e432f2923a2c7e8c344466
--- /dev/null
+++ b/thirdparty/cpm-openmpt.cmake
@@ -0,0 +1,289 @@
+CPMAddPackage(
+	NAME openmpt
+	VERSION 0.4.30
+	URL "https://github.com/OpenMPT/openmpt/archive/refs/tags/libopenmpt-0.4.30.zip"
+	DOWNLOAD_ONLY ON
+)
+
+if(openmpt_ADDED)
+	set(
+		openmpt_SOURCES
+
+		# minimp3
+		# -DMPT_WITH_MINIMP3
+		include/minimp3/minimp3.c
+
+		common/mptStringParse.cpp
+		common/mptLibrary.cpp
+		common/Logging.cpp
+		common/Profiler.cpp
+		common/version.cpp
+		common/mptCPU.cpp
+		common/ComponentManager.cpp
+		common/mptOS.cpp
+		common/serialization_utils.cpp
+		common/mptStringFormat.cpp
+		common/FileReader.cpp
+		common/mptWine.cpp
+		common/mptPathString.cpp
+		common/mptAlloc.cpp
+		common/mptUUID.cpp
+		common/mptTime.cpp
+		common/mptString.cpp
+		common/mptFileIO.cpp
+		common/mptStringBuffer.cpp
+		common/mptRandom.cpp
+		common/mptIO.cpp
+		common/misc_util.cpp
+
+		common/mptCRC.h
+		common/mptLibrary.h
+		common/mptIO.h
+		common/version.h
+		common/stdafx.h
+		common/ComponentManager.h
+		common/Endianness.h
+		common/mptStringFormat.h
+		common/mptMutex.h
+		common/mptUUID.h
+		common/mptExceptionText.h
+		common/BuildSettings.h
+		common/mptAlloc.h
+		common/mptTime.h
+		common/FileReaderFwd.h
+		common/Logging.h
+		common/mptException.h
+		common/mptWine.h
+		common/mptStringBuffer.h
+		common/misc_util.h
+		common/mptBaseMacros.h
+		common/mptMemory.h
+		common/mptFileIO.h
+		common/serialization_utils.h
+		common/mptSpan.h
+		common/mptThread.h
+		common/FlagSet.h
+		common/mptString.h
+		common/mptStringParse.h
+		common/mptBaseUtils.h
+		common/mptRandom.h
+		common/CompilerDetect.h
+		common/FileReader.h
+		common/mptAssert.h
+		common/mptPathString.h
+		common/Profiler.h
+		common/mptOS.h
+		common/mptBaseTypes.h
+		common/mptCPU.h
+		common/mptBufferIO.h
+		common/versionNumber.h
+
+		soundlib/WAVTools.cpp
+		soundlib/ITTools.cpp
+		soundlib/AudioCriticalSection.cpp
+		soundlib/Load_stm.cpp
+		soundlib/MixerLoops.cpp
+		soundlib/Load_dbm.cpp
+		soundlib/ModChannel.cpp
+		soundlib/Load_gdm.cpp
+		soundlib/Snd_fx.cpp
+		soundlib/Load_mid.cpp
+		soundlib/mod_specifications.cpp
+		soundlib/Snd_flt.cpp
+		soundlib/Load_psm.cpp
+		soundlib/Load_far.cpp
+		soundlib/patternContainer.cpp
+		soundlib/Load_med.cpp
+		soundlib/Load_dmf.cpp
+		soundlib/Paula.cpp
+		soundlib/modcommand.cpp
+		soundlib/Message.cpp
+		soundlib/SoundFilePlayConfig.cpp
+		soundlib/Load_uax.cpp
+		soundlib/plugins/PlugInterface.cpp
+		soundlib/plugins/LFOPlugin.cpp
+		soundlib/plugins/PluginManager.cpp
+		soundlib/plugins/DigiBoosterEcho.cpp
+		soundlib/plugins/dmo/DMOPlugin.cpp
+		soundlib/plugins/dmo/Flanger.cpp
+		soundlib/plugins/dmo/Distortion.cpp
+		soundlib/plugins/dmo/ParamEq.cpp
+		soundlib/plugins/dmo/Gargle.cpp
+		soundlib/plugins/dmo/I3DL2Reverb.cpp
+		soundlib/plugins/dmo/Compressor.cpp
+		soundlib/plugins/dmo/WavesReverb.cpp
+		soundlib/plugins/dmo/Echo.cpp
+		soundlib/plugins/dmo/Chorus.cpp
+		soundlib/Load_ams.cpp
+		soundlib/tuningbase.cpp
+		soundlib/ContainerUMX.cpp
+		soundlib/Load_ptm.cpp
+		soundlib/ContainerXPK.cpp
+		soundlib/SampleFormatMP3.cpp
+		soundlib/tuning.cpp
+		soundlib/Sndfile.cpp
+		soundlib/ContainerMMCMP.cpp
+		soundlib/Load_amf.cpp
+		soundlib/Load_669.cpp
+		soundlib/modsmp_ctrl.cpp
+		soundlib/Load_mtm.cpp
+		soundlib/OggStream.cpp
+		soundlib/Load_plm.cpp
+		soundlib/Tables.cpp
+		soundlib/Load_c67.cpp
+		soundlib/Load_mod.cpp
+		soundlib/Load_sfx.cpp
+		soundlib/Sndmix.cpp
+		soundlib/load_j2b.cpp
+		soundlib/ModSequence.cpp
+		soundlib/SampleFormatFLAC.cpp
+		soundlib/ModInstrument.cpp
+		soundlib/Load_mo3.cpp
+		soundlib/ModSample.cpp
+		soundlib/Dlsbank.cpp
+		soundlib/Load_itp.cpp
+		soundlib/UpgradeModule.cpp
+		soundlib/MIDIMacros.cpp
+		soundlib/ContainerPP20.cpp
+		soundlib/RowVisitor.cpp
+		soundlib/Load_imf.cpp
+		soundlib/SampleFormatVorbis.cpp
+		soundlib/Load_dsm.cpp
+		soundlib/Load_mt2.cpp
+		soundlib/MixerSettings.cpp
+		soundlib/S3MTools.cpp
+		soundlib/Load_xm.cpp
+		soundlib/MIDIEvents.cpp
+		soundlib/pattern.cpp
+		soundlib/Load_digi.cpp
+		soundlib/Load_s3m.cpp
+		soundlib/tuningCollection.cpp
+		soundlib/SampleIO.cpp
+		soundlib/Dither.cpp
+		soundlib/Load_mdl.cpp
+		soundlib/OPL.cpp
+		soundlib/WindowedFIR.cpp
+		soundlib/SampleFormats.cpp
+		soundlib/Load_wav.cpp
+		soundlib/Load_it.cpp
+		soundlib/UMXTools.cpp
+		soundlib/Load_stp.cpp
+		soundlib/Load_okt.cpp
+		soundlib/Load_ult.cpp
+		soundlib/MixFuncTable.cpp
+		soundlib/SampleFormatOpus.cpp
+		soundlib/Fastmix.cpp
+		soundlib/Tagging.cpp
+		soundlib/ITCompression.cpp
+		soundlib/Load_dtm.cpp
+		soundlib/MPEGFrame.cpp
+		soundlib/XMTools.cpp
+		soundlib/SampleFormatMediaFoundation.cpp
+		soundlib/InstrumentExtensions.cpp
+
+		soundlib/MixerInterface.h
+		soundlib/SoundFilePlayConfig.h
+		soundlib/ModSample.h
+		soundlib/MIDIEvents.h
+		soundlib/ModSampleCopy.h
+		soundlib/patternContainer.h
+		soundlib/ChunkReader.h
+		soundlib/ITCompression.h
+		soundlib/Dither.h
+		soundlib/S3MTools.h
+		soundlib/MPEGFrame.h
+		soundlib/WAVTools.h
+		soundlib/mod_specifications.h
+		soundlib/ITTools.h
+		soundlib/RowVisitor.h
+		soundlib/plugins/PluginMixBuffer.h
+		soundlib/plugins/PluginStructs.h
+		soundlib/plugins/LFOPlugin.h
+		soundlib/plugins/PlugInterface.h
+		soundlib/plugins/DigiBoosterEcho.h
+		soundlib/plugins/OpCodes.h
+		soundlib/plugins/dmo/Echo.h
+		soundlib/plugins/dmo/I3DL2Reverb.h
+		soundlib/plugins/dmo/WavesReverb.h
+		soundlib/plugins/dmo/ParamEq.h
+		soundlib/plugins/dmo/Gargle.h
+		soundlib/plugins/dmo/DMOPlugin.h
+		soundlib/plugins/dmo/Chorus.h
+		soundlib/plugins/dmo/Compressor.h
+		soundlib/plugins/dmo/Distortion.h
+		soundlib/plugins/dmo/Flanger.h
+		soundlib/plugins/PluginManager.h
+		soundlib/SampleIO.h
+		soundlib/Container.h
+		soundlib/ModSequence.h
+		soundlib/UMXTools.h
+		soundlib/Message.h
+		soundlib/modcommand.h
+		soundlib/XMTools.h
+		soundlib/Snd_defs.h
+		soundlib/MixFuncTable.h
+		soundlib/pattern.h
+		soundlib/modsmp_ctrl.h
+		soundlib/Tagging.h
+		soundlib/tuningcollection.h
+		soundlib/Mixer.h
+		soundlib/FloatMixer.h
+		soundlib/AudioCriticalSection.h
+		soundlib/Tables.h
+		soundlib/tuningbase.h
+		soundlib/WindowedFIR.h
+		soundlib/Sndfile.h
+		soundlib/Paula.h
+		soundlib/ModInstrument.h
+		soundlib/Dlsbank.h
+		soundlib/IntMixer.h
+		soundlib/OPL.h
+		soundlib/Resampler.h
+		soundlib/ModChannel.h
+		soundlib/MixerSettings.h
+		soundlib/AudioReadTarget.h
+		soundlib/MixerLoops.h
+		soundlib/tuning.h
+		soundlib/MIDIMacros.h
+		soundlib/OggStream.h
+		soundlib/Loaders.h
+		soundlib/BitReader.h
+		soundlib/opal.h
+
+		sounddsp/AGC.cpp
+		sounddsp/EQ.cpp
+		sounddsp/DSP.cpp
+		sounddsp/Reverb.cpp
+		sounddsp/Reverb.h
+		sounddsp/EQ.h
+		sounddsp/DSP.h
+		sounddsp/AGC.h
+
+		libopenmpt/libopenmpt_c.cpp
+		libopenmpt/libopenmpt_cxx.cpp
+		libopenmpt/libopenmpt_impl.cpp
+		libopenmpt/libopenmpt_ext_impl.cpp
+	)
+	list(TRANSFORM openmpt_SOURCES PREPEND "${openmpt_SOURCE_DIR}/")
+
+	# -DLIBOPENMPT_BUILD
+	configure_file("openmpt_svn_version.h" "svn_version.h")
+	add_library(openmpt "${SRB2_INTERNAL_LIBRARY_TYPE}" ${openmpt_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/svn_version.h)
+	if("${CMAKE_C_COMPILER_ID}" STREQUAL GNU OR "${CMAKE_C_COMPILER_ID}" STREQUAL Clang OR "${CMAKE_C_COMPILER_ID}" STREQUAL AppleClang)
+		target_compile_options(openmpt PRIVATE "-g0")
+	endif()
+	if("${CMAKE_SYSTEM_NAME}" STREQUAL Windows AND "${CMAKE_C_COMPILER_ID}" STREQUAL MSVC)
+		target_link_libraries(openmpt PRIVATE Rpcrt4)
+	endif()
+	target_compile_features(openmpt PRIVATE cxx_std_11)
+	target_compile_definitions(openmpt PRIVATE -DLIBOPENMPT_BUILD)
+
+	target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}/common")
+	target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}/src")
+	target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}/include")
+	target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}")
+	target_include_directories(openmpt PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
+
+	# I wish this wasn't necessary, but it is
+	target_include_directories(openmpt PUBLIC "${openmpt_SOURCE_DIR}")
+endif()
diff --git a/thirdparty/cpm-png.cmake b/thirdparty/cpm-png.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..f16ac037b0cc9c077f3ef315b67b430e68cf9b40
--- /dev/null
+++ b/thirdparty/cpm-png.cmake
@@ -0,0 +1,69 @@
+CPMAddPackage(
+	NAME png
+	VERSION 1.6.38
+	URL "https://github.com/glennrp/libpng/archive/refs/tags/v1.6.38.zip"
+	# png cmake build is broken on msys/mingw32
+	DOWNLOAD_ONLY YES
+)
+
+if(png_ADDED)
+	# Since png's cmake build is broken, we're going to create a target manually
+	set(
+		PNG_SOURCES
+		png.h
+		pngconf.h
+		pngpriv.h
+		pngdebug.h
+		pnginfo.h
+		pngstruct.h
+		png.c
+		pngerror.c
+		pngget.c
+		pngmem.c
+		pngpread.c
+		pngread.c
+		pngrio.c
+		pngrtran.c
+		pngrutil.c
+		pngset.c
+		pngtrans.c
+		pngwio.c
+		pngwrite.c
+		pngwtran.c
+		pngwutil.c
+	)
+	list(TRANSFORM PNG_SOURCES PREPEND "${png_SOURCE_DIR}/")
+
+	add_custom_command(
+		OUTPUT "${png_BINARY_DIR}/include/png.h" "${png_BINARY_DIR}/include/pngconf.h"
+		COMMAND ${CMAKE_COMMAND} -E copy "${png_SOURCE_DIR}/png.h" "${png_SOURCE_DIR}/pngconf.h" "${png_BINARY_DIR}/include"
+		DEPENDS "${png_SOURCE_DIR}/png.h" "${png_SOURCE_DIR}/pngconf.h"
+		VERBATIM
+	)
+	add_custom_command(
+		OUTPUT "${png_BINARY_DIR}/include/pnglibconf.h"
+		COMMAND ${CMAKE_COMMAND} -E copy "${png_SOURCE_DIR}/scripts/pnglibconf.h.prebuilt" "${png_BINARY_DIR}/include/pnglibconf.h"
+		DEPENDS "${png_SOURCE_DIR}/scripts/pnglibconf.h.prebuilt"
+		VERBATIM
+	)
+	list(
+		APPEND PNG_SOURCES
+		"${png_BINARY_DIR}/include/png.h"
+		"${png_BINARY_DIR}/include/pngconf.h"
+		"${png_BINARY_DIR}/include/pnglibconf.h"
+	)
+	add_library(png "${SRB2_INTERNAL_LIBRARY_TYPE}" ${PNG_SOURCES})
+
+	# Disable ARM NEON since having it automatic breaks libpng external build on clang for some reason
+	target_compile_definitions(png PRIVATE -DPNG_ARM_NEON_OPT=0)
+
+	# The png includes need to be available to consumers
+	target_include_directories(png PUBLIC "${png_BINARY_DIR}/include")
+
+	# ... and these also need to be present only for png build
+	target_include_directories(png PRIVATE "${ZLIB_SOURCE_DIR}")
+	target_include_directories(png PRIVATE "${ZLIB_BINARY_DIR}")
+	target_include_directories(png PRIVATE "${png_BINARY_DIR}")
+	target_link_libraries(png PRIVATE ZLIB::ZLIB)
+	add_library(PNG::PNG ALIAS png)
+endif()
diff --git a/thirdparty/cpm-sdl2-mixer.cmake b/thirdparty/cpm-sdl2-mixer.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..b7dfeae0d3eab254651f0c5e6e69e7af0626012e
--- /dev/null
+++ b/thirdparty/cpm-sdl2-mixer.cmake
@@ -0,0 +1,22 @@
+CPMAddPackage(
+	NAME SDL2_mixer
+	VERSION 2.6.2
+	URL "https://github.com/libsdl-org/SDL_mixer/archive/refs/tags/release-2.6.2.zip"
+	EXCLUDE_FROM_ALL ON
+	OPTIONS
+		"BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
+		"SDL2MIXER_INSTALL OFF"
+		"SDL2MIXER_DEPS_SHARED OFF"
+		"SDL2MIXER_SAMPLES OFF"
+		"SDL2MIXER_VENDORED ON"
+		"SDL2MIXER_FLAC ON"
+		"SDL2MIXER_FLAC_LIBFLAC OFF"
+		"SDL2MIXER_FLAC_DRFLAC ON"
+		"SDL2MIXER_MOD OFF"
+		"SDL2MIXER_MP3 ON"
+		"SDL2MIXER_MP3_DRMP3 ON"
+		"SDL2MIXER_MIDI ON"
+		"SDL2MIXER_OPUS OFF"
+		"SDL2MIXER_VORBIS STB"
+		"SDL2MIXER_WAVE ON"
+)
diff --git a/thirdparty/cpm-sdl2.cmake b/thirdparty/cpm-sdl2.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..58cf9afc2a8c9f443379625049ebd28446382b84
--- /dev/null
+++ b/thirdparty/cpm-sdl2.cmake
@@ -0,0 +1,13 @@
+CPMAddPackage(
+	NAME SDL2
+	VERSION 2.24.2
+	URL "https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.24.2.zip"
+	EXCLUDE_FROM_ALL ON
+	OPTIONS
+		"BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
+		"SDL_SHARED ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
+		"SDL_STATIC ${NOT_SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
+		"SDL_TEST OFF"
+		"SDL2_DISABLE_SDL2MAIN ON"
+		"SDL2_DISABLE_INSTALL ON"
+)
diff --git a/thirdparty/cpm-zlib.cmake b/thirdparty/cpm-zlib.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..5368366fd14a4246da476e48bb98ce708812646e
--- /dev/null
+++ b/thirdparty/cpm-zlib.cmake
@@ -0,0 +1,53 @@
+CPMAddPackage(
+	NAME ZLIB
+	VERSION 1.2.13
+	URL "https://github.com/madler/zlib/archive/refs/tags/v1.2.13.zip"
+	EXCLUDE_FROM_ALL
+	DOWNLOAD_ONLY YES
+)
+
+if(ZLIB_ADDED)
+	set(ZLIB_SRCS
+		crc32.h
+		deflate.h
+		gzguts.h
+		inffast.h
+		inffixed.h
+		inflate.h
+		inftrees.h
+		trees.h
+		zutil.h
+		adler32.c
+		compress.c
+		crc32.c
+		deflate.c
+		gzclose.c
+		gzlib.c
+		gzread.c
+		gzwrite.c
+		inflate.c
+		infback.c
+		inftrees.c
+		inffast.c
+		trees.c
+		uncompr.c
+		zutil.c
+	)
+	list(TRANSFORM ZLIB_SRCS PREPEND "${ZLIB_SOURCE_DIR}/")
+
+	configure_file("${ZLIB_SOURCE_DIR}/zlib.pc.cmakein" "${ZLIB_BINARY_DIR}/zlib.pc" @ONLY)
+	configure_file("${ZLIB_SOURCE_DIR}/zconf.h.cmakein" "${ZLIB_BINARY_DIR}/include/zconf.h" @ONLY)
+	configure_file("${ZLIB_SOURCE_DIR}/zlib.h" "${ZLIB_BINARY_DIR}/include/zlib.h" @ONLY)
+
+	add_library(ZLIB ${SRB2_INTERNAL_LIBRARY_TYPE} ${ZLIB_SRCS})
+	set_target_properties(ZLIB PROPERTIES
+		VERSION 1.2.13
+		OUTPUT_NAME "z"
+	)
+	target_include_directories(ZLIB PRIVATE "${ZLIB_SOURCE_DIR}")
+	target_include_directories(ZLIB PUBLIC "${ZLIB_BINARY_DIR}/include")
+	if(MSVC)
+		target_compile_definitions(ZLIB PRIVATE -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE)
+	endif()
+	add_library(ZLIB::ZLIB ALIAS ZLIB)
+endif()