CC ?= cc
CXX ?= c++
AR ?= ar

BUILD_DIR ?= build
TARGET := $(BUILD_DIR)/libcoduo_lnxded_recovered.a
EXE_BUILD_DIR ?= build-exe
EXE_TARGET := $(EXE_BUILD_DIR)/coduo_lnxded_recovered
EXE_I386_BUILD_DIR ?= build-i386
EXE_I386_TARGET := $(EXE_I386_BUILD_DIR)/coduo_lnxded_recovered
WINDOWS_I386_BUILD_DIR ?= build-windows-i386
WINDOWS_I386_TARGET := $(WINDOWS_I386_BUILD_DIR)/coduo_dedicated_recovered.exe
WINDOWS_I686_BUILD_DIR ?= build-windows-i686
WINDOWS_I686_TARGET := $(WINDOWS_I686_BUILD_DIR)/coduo_dedicated_recovered.exe
CODUO_DISABLE_SERVER_AUTH ?= 0
QCOMMON_DIR := ../shared-qcommon
QCOMMON_CSRCS := $(wildcard $(QCOMMON_DIR)/src/*.c)
MATH_DIR := ../shared-math
MATH_CSRCS := $(wildcard $(MATH_DIR)/src/*.c)
CODUO_DEFAULT_BEHAVIOR := linux
include ../build-mk/platform-behavior.mk
ALL_CSRCS := $(shell find src -name '*.c' ! -name '._*' | sort)
PUNKBUSTER_RECOVERY_SRCS := src/punkbuster/pb_server.c
CSRCS := $(filter-out $(PUNKBUSTER_RECOVERY_SRCS),$(ALL_CSRCS))
CXXSRCS := $(shell find src -name '*.cpp' ! -name '._*' | sort)
OBJS := $(patsubst src/%.c,$(BUILD_DIR)/%.o,$(CSRCS)) \
        $(patsubst src/%.cpp,$(BUILD_DIR)/%.o,$(CXXSRCS)) \
        $(patsubst $(QCOMMON_DIR)/src/%.c,$(BUILD_DIR)/qcommon/%.o,$(QCOMMON_CSRCS)) \
        $(patsubst $(MATH_DIR)/src/%.c,$(BUILD_DIR)/math/%.o,$(MATH_CSRCS))
SHARED_OBJS := $(patsubst src/%.c,$(BUILD_DIR)/shared/%.o,$(CSRCS)) \
               $(patsubst src/%.cpp,$(BUILD_DIR)/shared/%.o,$(CXXSRCS)) \
               $(patsubst $(QCOMMON_DIR)/src/%.c,$(BUILD_DIR)/shared/qcommon/%.o,$(QCOMMON_CSRCS)) \
               $(patsubst $(MATH_DIR)/src/%.c,$(BUILD_DIR)/shared/math/%.o,$(MATH_CSRCS))
EXE_OBJS := $(patsubst src/%.c,$(EXE_BUILD_DIR)/%.o,$(CSRCS)) \
            $(patsubst src/%.cpp,$(EXE_BUILD_DIR)/%.o,$(CXXSRCS)) \
            $(patsubst $(QCOMMON_DIR)/src/%.c,$(EXE_BUILD_DIR)/qcommon/%.o,$(QCOMMON_CSRCS)) \
            $(patsubst $(MATH_DIR)/src/%.c,$(EXE_BUILD_DIR)/math/%.o,$(MATH_CSRCS))
EXE_I386_OBJS := $(patsubst src/%.c,$(EXE_I386_BUILD_DIR)/%.o,$(CSRCS)) \
                 $(patsubst src/%.cpp,$(EXE_I386_BUILD_DIR)/%.o,$(CXXSRCS)) \
                 $(patsubst $(QCOMMON_DIR)/src/%.c,$(EXE_I386_BUILD_DIR)/qcommon/%.o,$(QCOMMON_CSRCS)) \
                 $(patsubst $(MATH_DIR)/src/%.c,$(EXE_I386_BUILD_DIR)/math/%.o,$(MATH_CSRCS))
WINDOWS_I386_OBJS := $(patsubst src/%.c,$(WINDOWS_I386_BUILD_DIR)/%.o,$(CSRCS)) \
                     $(patsubst src/%.cpp,$(WINDOWS_I386_BUILD_DIR)/%.o,$(CXXSRCS)) \
                     $(patsubst $(QCOMMON_DIR)/src/%.c,$(WINDOWS_I386_BUILD_DIR)/qcommon/%.o,$(QCOMMON_CSRCS)) \
                     $(patsubst $(MATH_DIR)/src/%.c,$(WINDOWS_I386_BUILD_DIR)/math/%.o,$(MATH_CSRCS))
WINDOWS_I686_OBJS := $(patsubst src/%.c,$(WINDOWS_I686_BUILD_DIR)/%.o,$(CSRCS)) \
                     $(patsubst src/%.cpp,$(WINDOWS_I686_BUILD_DIR)/%.o,$(CXXSRCS)) \
                     $(patsubst $(QCOMMON_DIR)/src/%.c,$(WINDOWS_I686_BUILD_DIR)/qcommon/%.o,$(QCOMMON_CSRCS)) \
                     $(patsubst $(MATH_DIR)/src/%.c,$(WINDOWS_I686_BUILD_DIR)/math/%.o,$(MATH_CSRCS))
DEPS := $(OBJS:.o=.d) $(SHARED_OBJS:.o=.d) $(EXE_OBJS:.o=.d) \
        $(EXE_I386_OBJS:.o=.d) $(WINDOWS_I386_OBJS:.o=.d) \
        $(WINDOWS_I686_OBJS:.o=.d)

CPPFLAGS += -Iinclude -I$(QCOMMON_DIR)/include -I$(MATH_DIR)/include \
	-I../shared-include \
	-I../vendor/softfloat/source/include \
	-DCODUO_ENABLE_PUNKBUSTER=0 \
	-DCODUO_DISABLE_SERVER_AUTH=$(CODUO_DISABLE_SERVER_AUTH)
# Stock i386 uses signed plain char, 32-bit enum-backed domains, and wrapping
# ADD/SUB/IMUL semantics. GCC, Clang, and MinGW support explicit switches for
# those rules, preventing host defaults or inherited flags from changing them.
CFLAGS += -std=c11 -Wall -Wextra -Werror -pedantic -fsigned-char \
	-fno-short-enums -fwrapv -MMD -MP
CXXFLAGS += -std=c++11 -Wall -Wextra -Werror -pedantic -Wno-c99-extensions \
	-fsigned-char -fno-short-enums -fwrapv -MMD -MP
NATIVE_CPPFLAGS ?=
NATIVE_CPPFLAGS += $(PLATFORM_BEHAVIOR_CPPFLAGS)
NATIVE_CXXFLAGS ?=
SHARED_CFLAGS += -fPIC
SHARED_CXXFLAGS += -fPIC
SHARED_LD ?= $(CXX)
SHARED_LDFLAGS += -shared
EXE_LD ?= $(CXX)
EXE_I386_CC ?= gcc -m32
EXE_I386_CXX ?= g++ -m32
EXE_I386_LD ?= $(EXE_I386_CXX)
EXE_I386_LIBS ?= -ldl -lm -pthread -lstdc++ -lz
MINGW32_CC ?= i686-w64-mingw32-gcc
MINGW32_CXX ?= i686-w64-mingw32-g++
WINDOWS_DEP_PREFIX ?=
WINDOWS_DEP_CPPFLAGS := $(if $(strip $(WINDOWS_DEP_PREFIX)),-I$(WINDOWS_DEP_PREFIX)/include)
WINDOWS_DEP_LDFLAGS := $(if $(strip $(WINDOWS_DEP_PREFIX)),-L$(WINDOWS_DEP_PREFIX)/lib)
WINDOWS_COMMON_CPPFLAGS := -D_WIN32_WINNT=0x0601 -DWINDOWS_BEHAVIOR \
	$(WINDOWS_DEP_CPPFLAGS)
WINDOWS_COMMON_FLOAT_FLAGS := -O0 -mfpmath=387 -fexcess-precision=fast
WINDOWS_COMMON_LDFLAGS := $(WINDOWS_DEP_LDFLAGS) -static-libgcc -static-libstdc++
WINDOWS_COMMON_LIBS ?= -lws2_32 -lz

UNAME_S := $(shell uname -s)
HOST_MACHINE := $(shell uname -m)

# x87 floating-point faithfulness policy (shared with the game DLL). Decides
# whether the native/exe build reproduces the original's x87 float rounding
# (-mfpmath=387 on GCC/x86), errors, or is an acknowledged non-exact build.
# See ../build-mk/x87-policy.mk and docs/fp-faithfulness.md.
X87_POLICY_ARCH := $(HOST_MACHINE)
X87_POLICY_CC := $(CC)
include ../build-mk/x87-policy.mk
NATIVE_CPPFLAGS += $(X87_ALLOW_CPPFLAGS)
ifeq ($(CODUO_FP_FAITHFUL),relaxed)
NATIVE_CPPFLAGS += -DEMULATE_X87=0
endif
NATIVE_FLOAT_FLAGS := $(X87_FLOAT_FLAGS)

# Phase-split for the fast (EMU_X87_DOUBLE) backend. The collision-geometry
# CONSTRUCTION (patch/soup build + its vector helpers) runs ONCE at map load, so
# its FP cost is amortized to nothing; only the per-frame TRACE path is hot.
# With EMU_X87_EXACT_GEOMETRY=1 these construction TUs are forced to the exact
# SoftFloat backend regardless of the query backend, so a double-backend build
# still loads BIT-IDENTICAL geometry (no ULP drift in the built planes) while
# traces stay native-double fast. The consumer must still link softfloat.a and
# add its -I (the construction TUs include softfloat.h). A trailing -D wins, so
# this overrides the backend the consumer set in CFLAGS for just these files.
ifeq ($(EMU_X87_EXACT_GEOMETRY),1)
EMU_X87_GEOMETRY_TUS := physics_collision/cm_patch_grid_helpers \
    core_math/core_math_geometry core_math/vector_normalize \
    core_math/vector_normalize2 core_math/cross_product
$(foreach t,$(EMU_X87_GEOMETRY_TUS),$(eval \
    $(EXE_BUILD_DIR)/$(t).o: NATIVE_FLOAT_FLAGS += \
        -UEMULATE_X87_BACKEND -DEMULATE_X87_BACKEND=EMU_X87_SOFTFLOAT))
endif

ifeq ($(UNAME_S),Darwin)
SHARED_EXT ?= dylib
UNRESOLVED_ENGINE_LDFLAGS ?= -Wl,-undefined,dynamic_lookup
ENGINE_IMPORT_LIBS ?= -lm -pthread -lz
else
SHARED_EXT ?= so
UNRESOLVED_ENGINE_LDFLAGS ?=
ENGINE_IMPORT_LIBS ?= -ldl -lm -pthread -lstdc++ -lz
CPPFLAGS += -D_GNU_SOURCE
NATIVE_CXXFLAGS += -Wno-pedantic
endif

SHARED_TARGET := $(BUILD_DIR)/libcoduo_lnxded_recovered.$(SHARED_EXT)

.PHONY: all check objects shared shared-check-link exe exe-i386 windows \
	windows-i386 windows-i686 native64-check native64-shared native64-exe \
	status clean help

all: objects

check: objects

objects: $(TARGET)

shared: $(SHARED_TARGET)

shared-check-link: shared

exe: $(EXE_TARGET)

exe-i386: $(EXE_I386_TARGET)

windows: windows-i686

windows-i386: $(WINDOWS_I386_TARGET)

windows-i686: $(WINDOWS_I686_TARGET)

native64-check: check

native64-shared: shared-check-link

native64-exe: exe

status:
	@printf '%s\n' 'Public export: private recovery status checker is not included.'

clean:
	rm -r $(BUILD_DIR)

help:
	@printf '%s\n' \
	  'Targets: check native64-check native64-shared native64-exe shared-check-link exe exe-i386 windows-i386 windows-i686 clean' \
	  '         CODUO_DISABLE_SERVER_AUTH=1 disables external server authorization.' \
	  '         CODUO_FP_FAITHFUL=auto|strict|relaxed controls x87 float faithfulness;' \
	  '           relaxed builds on clang / ARM (macOS) but is NOT a bit-exact rebuild.' \
	  '         WINDOWS_DEP_PREFIX=/path supplies MinGW zlib headers and libraries.' \
	  '           See docs/fp-faithfulness.md.'

$(BUILD_DIR):
	mkdir -p $(BUILD_DIR)

$(EXE_I386_BUILD_DIR):
	mkdir -p $(EXE_I386_BUILD_DIR)

$(EXE_BUILD_DIR):
	mkdir -p $(EXE_BUILD_DIR)

$(WINDOWS_I386_BUILD_DIR):
	mkdir -p $(WINDOWS_I386_BUILD_DIR)

$(WINDOWS_I686_BUILD_DIR):
	mkdir -p $(WINDOWS_I686_BUILD_DIR)

$(BUILD_DIR)/%.o: src/%.c
	mkdir -p $(dir $@)
	$(CC) $(CPPFLAGS) $(NATIVE_CPPFLAGS) $(CFLAGS) $(NATIVE_FLOAT_FLAGS) -c $< -o $@

$(BUILD_DIR)/qcommon/%.o: $(QCOMMON_DIR)/src/%.c
	mkdir -p $(dir $@)
	$(CC) $(CPPFLAGS) $(NATIVE_CPPFLAGS) $(CFLAGS) $(NATIVE_FLOAT_FLAGS) -c $< -o $@

$(BUILD_DIR)/math/%.o: $(MATH_DIR)/src/%.c
	mkdir -p $(dir $@)
	$(CC) $(CPPFLAGS) $(NATIVE_CPPFLAGS) $(CFLAGS) $(NATIVE_FLOAT_FLAGS) -c $< -o $@

$(BUILD_DIR)/%.o: src/%.cpp
	mkdir -p $(dir $@)
	$(CXX) $(CPPFLAGS) $(NATIVE_CPPFLAGS) $(CXXFLAGS) $(NATIVE_CXXFLAGS) $(NATIVE_FLOAT_FLAGS) -c $< -o $@

$(BUILD_DIR)/shared/%.o: src/%.c
	mkdir -p $(dir $@)
	$(CC) $(CPPFLAGS) $(NATIVE_CPPFLAGS) $(CFLAGS) $(SHARED_CFLAGS) $(NATIVE_FLOAT_FLAGS) -c $< -o $@

$(BUILD_DIR)/shared/qcommon/%.o: $(QCOMMON_DIR)/src/%.c
	mkdir -p $(dir $@)
	$(CC) $(CPPFLAGS) $(NATIVE_CPPFLAGS) $(CFLAGS) $(SHARED_CFLAGS) $(NATIVE_FLOAT_FLAGS) -c $< -o $@

$(BUILD_DIR)/shared/math/%.o: $(MATH_DIR)/src/%.c
	mkdir -p $(dir $@)
	$(CC) $(CPPFLAGS) $(NATIVE_CPPFLAGS) $(CFLAGS) $(SHARED_CFLAGS) $(NATIVE_FLOAT_FLAGS) -c $< -o $@

$(BUILD_DIR)/shared/%.o: src/%.cpp
	mkdir -p $(dir $@)
	$(CXX) $(CPPFLAGS) $(NATIVE_CPPFLAGS) $(CXXFLAGS) $(NATIVE_CXXFLAGS) $(SHARED_CXXFLAGS) $(NATIVE_FLOAT_FLAGS) -c $< -o $@

$(EXE_BUILD_DIR)/%.o: src/%.c
	mkdir -p $(dir $@)
	$(CC) $(CPPFLAGS) $(NATIVE_CPPFLAGS) $(CFLAGS) $(NATIVE_FLOAT_FLAGS) -c $< -o $@

$(EXE_BUILD_DIR)/qcommon/%.o: $(QCOMMON_DIR)/src/%.c
	mkdir -p $(dir $@)
	$(CC) $(CPPFLAGS) $(NATIVE_CPPFLAGS) $(CFLAGS) $(NATIVE_FLOAT_FLAGS) -c $< -o $@

$(EXE_BUILD_DIR)/math/%.o: $(MATH_DIR)/src/%.c
	mkdir -p $(dir $@)
	$(CC) $(CPPFLAGS) $(NATIVE_CPPFLAGS) $(CFLAGS) $(NATIVE_FLOAT_FLAGS) -c $< -o $@

$(EXE_BUILD_DIR)/%.o: src/%.cpp
	mkdir -p $(dir $@)
	$(CXX) $(CPPFLAGS) $(NATIVE_CPPFLAGS) $(CXXFLAGS) $(NATIVE_CXXFLAGS) $(NATIVE_FLOAT_FLAGS) -c $< -o $@

# 32-bit i386 is always x87; -fexcess-precision=fast gives stock's per-assignment
# float rounding (the -std=c11 default -fexcess-precision=standard keeps 80-bit
# x87 values live across float assignments and diverges from stock on FP ties —
# see ../build-mk/x87-policy.mk). The retail i386 graph lowers comparisons
# through the x87 status word; -march=i386 prevents modern GCC from substituting
# the later FCOMI/FUCOMI instruction family.
$(EXE_I386_BUILD_DIR)/%.o: src/%.c
	mkdir -p $(dir $@)
	$(EXE_I386_CC) $(CPPFLAGS) $(CFLAGS) -march=i386 \
		$(PLATFORM_BEHAVIOR_CPPFLAGS) -fexcess-precision=fast -c $< -o $@

$(EXE_I386_BUILD_DIR)/qcommon/%.o: $(QCOMMON_DIR)/src/%.c
	mkdir -p $(dir $@)
	$(EXE_I386_CC) $(CPPFLAGS) $(CFLAGS) -march=i386 \
		$(PLATFORM_BEHAVIOR_CPPFLAGS) -fexcess-precision=fast -c $< -o $@

$(EXE_I386_BUILD_DIR)/math/%.o: $(MATH_DIR)/src/%.c
	mkdir -p $(dir $@)
	$(EXE_I386_CC) $(CPPFLAGS) $(CFLAGS) -march=i386 \
		$(PLATFORM_BEHAVIOR_CPPFLAGS) -fexcess-precision=fast -c $< -o $@

$(EXE_I386_BUILD_DIR)/%.o: src/%.cpp
	mkdir -p $(dir $@)
	$(EXE_I386_CXX) $(CPPFLAGS) $(filter-out -pedantic,$(CXXFLAGS)) \
		-Wno-pedantic -march=i386 $(PLATFORM_BEHAVIOR_CPPFLAGS) \
		-fexcess-precision=fast -c $< -o $@

$(WINDOWS_I386_BUILD_DIR)/%.o: src/%.c
	mkdir -p $(dir $@)
	$(MINGW32_CC) $(CPPFLAGS) $(WINDOWS_COMMON_CPPFLAGS) $(CFLAGS) \
		-march=i386 $(WINDOWS_COMMON_FLOAT_FLAGS) -c $< -o $@

$(WINDOWS_I386_BUILD_DIR)/qcommon/%.o: $(QCOMMON_DIR)/src/%.c
	mkdir -p $(dir $@)
	$(MINGW32_CC) $(CPPFLAGS) $(WINDOWS_COMMON_CPPFLAGS) $(CFLAGS) \
		-march=i386 $(WINDOWS_COMMON_FLOAT_FLAGS) -c $< -o $@

$(WINDOWS_I386_BUILD_DIR)/math/%.o: $(MATH_DIR)/src/%.c
	mkdir -p $(dir $@)
	$(MINGW32_CC) $(CPPFLAGS) $(WINDOWS_COMMON_CPPFLAGS) $(CFLAGS) \
		-march=i386 $(WINDOWS_COMMON_FLOAT_FLAGS) -c $< -o $@

$(WINDOWS_I386_BUILD_DIR)/%.o: src/%.cpp
	mkdir -p $(dir $@)
	$(MINGW32_CXX) $(CPPFLAGS) $(WINDOWS_COMMON_CPPFLAGS) \
		$(filter-out -pedantic,$(CXXFLAGS)) -Wno-pedantic -march=i386 \
		$(WINDOWS_COMMON_FLOAT_FLAGS) -c $< -o $@

$(WINDOWS_I686_BUILD_DIR)/%.o: src/%.c
	mkdir -p $(dir $@)
	$(MINGW32_CC) $(CPPFLAGS) $(WINDOWS_COMMON_CPPFLAGS) $(CFLAGS) \
		-march=i686 $(WINDOWS_COMMON_FLOAT_FLAGS) -c $< -o $@

$(WINDOWS_I686_BUILD_DIR)/qcommon/%.o: $(QCOMMON_DIR)/src/%.c
	mkdir -p $(dir $@)
	$(MINGW32_CC) $(CPPFLAGS) $(WINDOWS_COMMON_CPPFLAGS) $(CFLAGS) \
		-march=i686 $(WINDOWS_COMMON_FLOAT_FLAGS) -c $< -o $@

$(WINDOWS_I686_BUILD_DIR)/math/%.o: $(MATH_DIR)/src/%.c
	mkdir -p $(dir $@)
	$(MINGW32_CC) $(CPPFLAGS) $(WINDOWS_COMMON_CPPFLAGS) $(CFLAGS) \
		-march=i686 $(WINDOWS_COMMON_FLOAT_FLAGS) -c $< -o $@

$(WINDOWS_I686_BUILD_DIR)/%.o: src/%.cpp
	mkdir -p $(dir $@)
	$(MINGW32_CXX) $(CPPFLAGS) $(WINDOWS_COMMON_CPPFLAGS) \
		$(filter-out -pedantic,$(CXXFLAGS)) -Wno-pedantic -march=i686 \
		$(WINDOWS_COMMON_FLOAT_FLAGS) -c $< -o $@

$(TARGET): $(OBJS) | $(BUILD_DIR)
	@if [ -e $@ ]; then rm $@; fi
	$(AR) rcs $@ $^

$(SHARED_TARGET): $(SHARED_OBJS) | $(BUILD_DIR)
	$(SHARED_LD) $(SHARED_LDFLAGS) $(UNRESOLVED_ENGINE_LDFLAGS) -o $@ $^ $(ENGINE_IMPORT_LIBS)

$(EXE_TARGET): $(EXE_OBJS) | $(EXE_BUILD_DIR)
	$(EXE_LD) -o $@ $^ $(ENGINE_IMPORT_LIBS)

$(EXE_I386_TARGET): $(EXE_I386_OBJS) | $(EXE_I386_BUILD_DIR)
	$(EXE_I386_LD) -o $@ $^ $(EXE_I386_LIBS)

$(WINDOWS_I386_TARGET): $(WINDOWS_I386_OBJS) | $(WINDOWS_I386_BUILD_DIR)
	$(MINGW32_CXX) -o $@ $^ $(WINDOWS_COMMON_LDFLAGS) $(WINDOWS_COMMON_LIBS)

$(WINDOWS_I686_TARGET): $(WINDOWS_I686_OBJS) | $(WINDOWS_I686_BUILD_DIR)
	$(MINGW32_CXX) -o $@ $^ $(WINDOWS_COMMON_LDFLAGS) $(WINDOWS_COMMON_LIBS)

-include $(DEPS)
