BUILD_DIR := build

# Prefer system QEMU on Linux/Ubuntu; keep Apple Silicon path as fallback.
# Just the binary path here -- display flags (see the `run` target's own
# DISPLAY_BACKEND, which is what actually controls the GTK menu bar/
# SDL/etc) do NOT belong baked into this variable: both run's headless
# branch and run-headless below already append their own `-display none`
# after $(QEMU), so anything appended here would silently conflict with
# (and lose to) whichever `-display` flag comes later on the command
# line -- QEMU takes the last one specified, making an earlier one dead.
UNAME_S := $(shell uname -s 2>/dev/null || echo unknown)
ifeq ($(UNAME_S),Darwin)
  QEMU ?= /opt/homebrew/bin/qemu-system-x86_64
else
  QEMU ?= $(shell command -v qemu-system-x86_64 2>/dev/null || echo /usr/bin/qemu-system-x86_64)
endif

# Toolchain (override with make CC=... LD=... NASM=... if needed)
CC ?= x86_64-linux-gnu-gcc
LD ?= x86_64-linux-gnu-ld
NASM ?= nasm
OBJCOPY ?= objcopy

CFLAGS := -m32 -ffreestanding -fno-pic -fno-pie -fno-stack-protector -mno-mmx -mno-sse -mno-sse2 -msoft-float -nostdlib -nostdinc -Wall -Wextra -O2 -Ikernel/include
LDFLAGS := -m elf_i386 -T kernel/linker.ld --oformat binary

# The UEFI boot path (build/BOOTX64.EFI, see boot/efi/) is a SEPARATE
# 64-bit build -- UEFI applications run in long mode regardless of the
# kernel's own 32-bit protected-mode design (see boot/efi/efi_stub.c's
# file comment). Reuses the same $(CC)/$(LD)/$(OBJCOPY) binaries (this
# environment has no clang/mingw-w64/gnu-efi and no package manager to
# install any -- see the plan file's Part 1), just without -m32 and with
# a different flag set targeting a freestanding PE32+ EFI application:
# -fpic (position-independent, since firmware may load this anywhere)
# and -fshort-wchar (CHAR16/L"..." must be 16-bit UTF-16LE per the UEFI
# spec; this gcc target's wchar_t defaults to 32-bit without this flag,
# which would silently corrupt every string passed to firmware).
EFICFLAGS := -ffreestanding -fpic -fshort-wchar -mno-red-zone -fno-stack-protector \
             -mno-mmx -mno-sse -mno-sse2 -nostdlib -nostdinc -Wall -Wextra -O2 -Iboot/efi

.PHONY: all clean run run-headless assets seconddisk

all: $(BUILD_DIR)/copperos.img

assets: $(BUILD_DIR)/assets.stamp

$(BUILD_DIR)/assets.stamp: scripts/build_assets.sh filemanager.png internetexplorer.png activitymanager.png paint.png systemsettings.png systemupdates.png calculator.png txteditor.png bootupscreen.png bootupscreenLog.png cursor.png cursorselected.png colorfulsnake.png cstore.png wallpaper.png apps/DISKMANAGER/diskmanager.png
	sh scripts/build_assets.sh
	touch $(BUILD_DIR)/assets.stamp

$(BUILD_DIR)/kernel_entry.o: kernel/arch/kernel_entry.asm
	$(NASM) -f elf32 -o $(BUILD_DIR)/kernel_entry.o kernel/arch/kernel_entry.asm

$(BUILD_DIR)/idt.o: kernel/arch/idt.asm
	$(NASM) -f elf32 -o $(BUILD_DIR)/idt.o kernel/arch/idt.asm

$(BUILD_DIR)/wake.o: kernel/arch/wake.asm
	$(NASM) -f elf32 -o $(BUILD_DIR)/wake.o kernel/arch/wake.asm

$(BUILD_DIR)/s3.o: kernel/arch/s3.asm
	$(NASM) -f elf32 -o $(BUILD_DIR)/s3.o kernel/arch/s3.asm


# assets.stamp is a REAL (not order-only) prerequisite here on purpose:
# assets.asm just incbins whatever's already sitting in build/assets/*
# (see kernel/assets/assets.asm) without naming those files directly, so
# Make has no other way to know assets.o needs relinking after e.g. a
# wallpaper-only change re-runs build_assets.sh -- an order-only `|`
# prerequisite (what this used to be) only sequences assets.stamp before
# assets.o on a from-scratch build, it does NOT make a newer assets.stamp
# invalidate an assets.o that already exists, which silently left a
# stale (and, for a same-named-but-different-content wallpaper.png, an
# actively *wrong*) assets.o linked into kernel.bin after any asset-only
# edit + `make assets` + `make`.
$(BUILD_DIR)/assets.o: kernel/assets/assets.asm $(BUILD_DIR)/assets.stamp
	$(NASM) -f elf32 -o $(BUILD_DIR)/assets.o kernel/assets/assets.asm

$(BUILD_DIR)/kernel.o: kernel/kernel.c kernel/include/bootinfo.h kernel/include/types.h kernel/include/app.h kernel/include/app_registry.h kernel/include/kapi.h kernel/drivers/filesystem.h kernel/drivers/speaker.h kernel/drivers/ata.h
	$(CC) $(CFLAGS) -c -o $(BUILD_DIR)/kernel.o kernel/kernel.c

$(BUILD_DIR)/registry.o: kernel/registry.c kernel/include/app.h kernel/include/app_registry.h kernel/include/kapi.h
	$(CC) $(CFLAGS) -c -o $(BUILD_DIR)/registry.o kernel/registry.c

$(BUILD_DIR)/ata.o: kernel/drivers/ata.c kernel/drivers/ata.h kernel/drivers/ahci.h
	$(CC) $(CFLAGS) -c -o $(BUILD_DIR)/ata.o kernel/drivers/ata.c

$(BUILD_DIR)/ahci.o: kernel/drivers/ahci.c kernel/drivers/ahci.h kernel/drivers/ata.h kernel/drivers/pci.h
	$(CC) $(CFLAGS) -c -o $(BUILD_DIR)/ahci.o kernel/drivers/ahci.c

$(BUILD_DIR)/speaker.o: kernel/drivers/speaker.c kernel/drivers/speaker.h kernel/drivers/pit.h
	$(CC) $(CFLAGS) -c -o $(BUILD_DIR)/speaker.o kernel/drivers/speaker.c

$(BUILD_DIR)/pit.o: kernel/drivers/pit.c kernel/drivers/pit.h
	$(CC) $(CFLAGS) -c -o $(BUILD_DIR)/pit.o kernel/drivers/pit.c

$(BUILD_DIR)/filesystem.o: kernel/drivers/filesystem.c kernel/drivers/filesystem.h kernel/drivers/ata.h kernel/crypto/aes256.h
	$(CC) $(CFLAGS) -c -o $(BUILD_DIR)/filesystem.o kernel/drivers/filesystem.c

$(BUILD_DIR)/diskfs.o: kernel/drivers/diskfs.c kernel/drivers/diskfs.h kernel/drivers/filesystem.h kernel/drivers/ata.h kernel/crypto/aes256.h
	$(CC) $(CFLAGS) -c -o $(BUILD_DIR)/diskfs.o kernel/drivers/diskfs.c

$(BUILD_DIR)/aes256.o: kernel/crypto/aes256.c kernel/crypto/aes256.h
	$(CC) $(CFLAGS) -c -o $(BUILD_DIR)/aes256.o kernel/crypto/aes256.c

$(BUILD_DIR)/inflate.o: kernel/gfx/inflate.c kernel/gfx/inflate.h
	$(CC) $(CFLAGS) -c -o $(BUILD_DIR)/inflate.o kernel/gfx/inflate.c

$(BUILD_DIR)/png.o: kernel/gfx/png.c kernel/gfx/png.h kernel/gfx/inflate.h
	$(CC) $(CFLAGS) -c -o $(BUILD_DIR)/png.o kernel/gfx/png.c

$(BUILD_DIR)/pci.o: kernel/drivers/pci.c kernel/drivers/pci.h
	$(CC) $(CFLAGS) -c -o $(BUILD_DIR)/pci.o kernel/drivers/pci.c

$(BUILD_DIR)/acpi.o: kernel/drivers/acpi.c kernel/drivers/acpi.h
	$(CC) $(CFLAGS) -c -o $(BUILD_DIR)/acpi.o kernel/drivers/acpi.c

NET_SRCS := \
	kernel/net/net.c \
	kernel/net/virtio_net.c \
	kernel/net/rtl8139.c \
	kernel/net/nic.c \
	kernel/net/arp.c \
	kernel/net/udp.c \
	kernel/net/dns.c \
	kernel/net/tcp.c \
	kernel/net/http.c \
	kernel/net/netstack.c

NET_OBJS := $(patsubst kernel/net/%.c,$(BUILD_DIR)/net/%.o,$(NET_SRCS))

$(BUILD_DIR)/net/%.o: kernel/net/%.c kernel/net/net.h kernel/drivers/pci.h
	@mkdir -p $(dir $@)
	$(CC) $(CFLAGS) -c -o $@ $<

# Every app lives under apps/<NAME>/<name>.c and is compiled as its own
# translation unit, registering one AppDescriptor (see kernel/include/app.h).
# Add new entries here as apps are migrated/added (Snake, Image Previewer,
# ccTermiX, etc. land in later phases once their logic is real).
APP_SRCS := \
	apps/FILEEXPLORER/fileexplorer.c \
	apps/INTERNETEXPLORER/internetexplorer.c \
	apps/ACTIVITYMANAGER/activitymanager.c \
	apps/PAINT/paint.c \
	apps/SYSTEMSETTINGS/settingapp.c \
	apps/SYSTEMUPDATES/systemupdates.c \
	apps/CALCULATOR/calculator.c \
	apps/TXTEDITOR/txteditor.c \
	apps/CSTORE/cstore.c \
	apps/SNAKE/snake.c \
	apps/CCTERMIX/cctermix.c \
	apps/IMAGEPREVIEWER/imagepreviewer.c \
	apps/DISKMANAGER/diskmanager.c

APP_OBJS := $(patsubst apps/%.c,$(BUILD_DIR)/apps/%.o,$(APP_SRCS))

$(BUILD_DIR)/apps/%.o: apps/%.c kernel/include/app.h kernel/include/app_registry.h kernel/include/kapi.h
	@mkdir -p $(dir $@)
	$(CC) $(CFLAGS) -c -o $@ $<

# kernel_entry.o MUST be the first input file in the link line below --
# kernel/linker.ld's `.text : { *(.text*) ... }` has no explicit file
# ordering, so the FIRST object file's .text section is what lands at
# the fixed load address 0x10000 the bootloader jumps to; kernel_entry.o
# contains _start, which must be exactly there. Every other .o (idt.o
# included) is safe to add anywhere AFTER it.
$(BUILD_DIR)/kernel.bin: $(BUILD_DIR)/kernel_entry.o $(BUILD_DIR)/assets.o $(BUILD_DIR)/kernel.o $(BUILD_DIR)/idt.o $(BUILD_DIR)/wake.o $(BUILD_DIR)/s3.o $(BUILD_DIR)/registry.o $(BUILD_DIR)/ata.o $(BUILD_DIR)/ahci.o $(BUILD_DIR)/speaker.o $(BUILD_DIR)/pit.o $(BUILD_DIR)/filesystem.o $(BUILD_DIR)/diskfs.o $(BUILD_DIR)/aes256.o $(BUILD_DIR)/inflate.o $(BUILD_DIR)/png.o $(BUILD_DIR)/pci.o $(BUILD_DIR)/acpi.o $(NET_OBJS) $(APP_OBJS) kernel/linker.ld
	$(LD) $(LDFLAGS) -o $(BUILD_DIR)/kernel.bin $(BUILD_DIR)/kernel_entry.o $(BUILD_DIR)/kernel.o $(BUILD_DIR)/idt.o $(BUILD_DIR)/wake.o $(BUILD_DIR)/s3.o $(BUILD_DIR)/registry.o $(BUILD_DIR)/ata.o $(BUILD_DIR)/ahci.o $(BUILD_DIR)/speaker.o $(BUILD_DIR)/pit.o $(BUILD_DIR)/filesystem.o $(BUILD_DIR)/diskfs.o $(BUILD_DIR)/aes256.o $(BUILD_DIR)/inflate.o $(BUILD_DIR)/png.o $(BUILD_DIR)/pci.o $(BUILD_DIR)/acpi.o $(NET_OBJS) $(APP_OBJS) $(BUILD_DIR)/assets.o

# boot/bootloader/stage2.asm's kernel loader advances a 16-bit real-mode
# segment register by 512 bytes per disk sector; that register wraps at
# ~983KB (0xFFFF-0x1000 segment units * 16 bytes/unit), silently
# corrupting the load past that point. 900KB leaves comfortable headroom
# below that wraparound while still giving plenty of room to grow --
# this guard fails the build loudly instead of ever shipping a silently
# corrupt image.
$(BUILD_DIR)/kernel_sectors.inc: $(BUILD_DIR)/kernel.bin
	# Linux/macOS compatible byte size
	# Linux: stat -c%s FILE, macOS: stat -f%z FILE
	@SIZE=$$(stat -c%s $(BUILD_DIR)/kernel.bin 2>/dev/null || stat -f%z $(BUILD_DIR)/kernel.bin); \
	if [ $$SIZE -gt 921600 ]; then \
		echo "ERROR: build/kernel.bin is $$SIZE bytes, over the 900KB safety ceiling."; \
		echo "  See the comment above this rule in the Makefile: stage2.asm's"; \
		echo "  16-bit segment-based kernel loader wraps at ~983KB. Either trim"; \
		echo "  the kernel back down, or give stage2.asm's load_kernel an"; \
		echo "  unreal-mode 32-bit copy to remove the ceiling entirely."; \
		exit 1; \
	fi; \
	printf 'KERNEL_SECTORS equ %d\n' $$(( ($$SIZE + 511) / 512 )) > $(BUILD_DIR)/kernel_sectors.inc

# STAGE2_SECTORS (32, see boot/bootloader/stage1.asm) is a fixed disk-sector
# reservation, not computed from stage2's actual size -- that would be a
# circular build dependency (stage2 needs KERNEL_LBA_START, itself derived
# from stage2's own size). This guard instead asserts the fixed budget is
# never exceeded; growing stage2.asm past it needs STAGE2_SECTORS raised in
# stage1.asm AND KERNEL_LBA_START (1 + STAGE2_SECTORS) recomputed to match
# in stage2.asm, or the kernel will be read from the wrong disk sector.
$(BUILD_DIR)/stage2.bin: boot/bootloader/stage2.asm $(BUILD_DIR)/kernel_sectors.inc
	$(NASM) -f bin -I . -o $(BUILD_DIR)/stage2.bin boot/bootloader/stage2.asm
	@SIZE=$$(stat -c%s $(BUILD_DIR)/stage2.bin 2>/dev/null || stat -f%z $(BUILD_DIR)/stage2.bin); \
	if [ $$SIZE -gt 16384 ]; then \
		echo "ERROR: build/stage2.bin is $$SIZE bytes, over the 16384-byte"; \
		echo "  (32-sector) budget boot/bootloader/stage1.asm reserves for it."; \
		echo "  See the comment above this rule in the Makefile."; \
		exit 1; \
	fi

$(BUILD_DIR)/boot.bin: boot/bootloader/stage1.asm $(BUILD_DIR)/stage2.bin
	$(NASM) -f bin -I . -o $(BUILD_DIR)/boot.bin boot/bootloader/stage1.asm

# 16MB: the filesystem's on-disk region starts at LBA 8192 (4MB in, see
# kernel/drivers/filesystem.c) with generous headroom past both the kernel
# image and the whole entry table + data region -- well under 16MB.
# Disk layout: sector 0 = stage1 (boot.bin), sectors 1-32 = stage2, sector
# 33 onward = the kernel (see KERNEL_LBA_START in stage2.asm).
$(BUILD_DIR)/copperos.img: $(BUILD_DIR)/boot.bin $(BUILD_DIR)/stage2.bin $(BUILD_DIR)/kernel.bin
	dd if=/dev/zero of=$(BUILD_DIR)/copperos.img bs=1M count=16
	dd if=$(BUILD_DIR)/boot.bin of=$(BUILD_DIR)/copperos.img conv=notrunc
	dd if=$(BUILD_DIR)/stage2.bin of=$(BUILD_DIR)/copperos.img bs=512 seek=1 conv=notrunc
	dd if=$(BUILD_DIR)/kernel.bin of=$(BUILD_DIR)/copperos.img bs=512 seek=33 conv=notrunc

# A blank, all-zero second disk to test Disk Manager / the removable-disk
# desktop icon against -- this driver has no hot-plug interrupt (see
# kernel/kernel.c's removable_disk_rescan()), so testing "a second disk is
# present" means attaching one before boot, not inserting one at runtime.
# All-zero means diskfs_mount() correctly reports it as unformatted (no
# magic match), matching a genuinely blank/foreign disk -- exercise
# Format from Disk Manager to turn it into a real CopperOS-formatted one.
$(BUILD_DIR)/seconddisk.img:
	dd if=/dev/zero of=$(BUILD_DIR)/seconddisk.img bs=1M count=16

seconddisk: $(BUILD_DIR)/seconddisk.img

# -audiodev/-machine pcspk-audiodev= route the guest's PC speaker (port
# 0x61 + PIT channel 2, see kernel/drivers/speaker.c) to host audio via
# QEMU's SDL backend; this flag syntax needs QEMU 5.0+. On real hardware
# no such wiring is needed at all -- the speaker is just there.
#
# Set COPPEROS_SECOND_DISK=build/seconddisk.img (see the `seconddisk`
# target above) to also attach a second disk on the secondary IDE bus's
# master slot -- explicit bus=ide.1,unit=0 placement (not a second bare
# -drive) so it reliably lands where kernel/kernel.c's removable-disk scan
# expects it (bus/drive index 2 in ata_identify_all()'s fixed slot order),
# rather than depending on QEMU's default drive-to-slot auto-assignment.
run: $(BUILD_DIR)/copperos.img
	@RESOLUTION=$${COPPEROS_RESOLUTION:-1280x720}; \
	DISPLAY_BACKEND=$${COPPEROS_DISPLAY_BACKEND:-gtk,show-menubar=on}; \
	NIC=$${COPPEROS_NIC:-user,model=virtio-net-pci,mac=52:54:00:12:34:56}; \
	VGA_DEVICE=$${COPPEROS_VGA:-VGA}; \
	SECOND_DISK_ARGS=""; \
	if [ -n "$${COPPEROS_SECOND_DISK:-}" ]; then \
		SECOND_DISK_ARGS="-drive if=none,format=raw,file=$$COPPEROS_SECOND_DISK,id=seconddisk -device ide-hd,drive=seconddisk,bus=ide.1,unit=0"; \
	fi; \
	if [ -n "$$DISPLAY" ]; then \
		WIDTH=$${RESOLUTION%x*}; HEIGHT=$${RESOLUTION#*x}; \
		if [ -z "$$WIDTH" ] || [ -z "$$HEIGHT" ]; then echo "Bad format. Use WIDTHxHEIGHT"; exit 1; fi; \
		echo "Launching CopperOS at $$RESOLUTION (display: $$DISPLAY_BACKEND, nic: $$NIC, vga: $$VGA_DEVICE)"; \
		$(QEMU) -drive format=raw,file=$(BUILD_DIR)/copperos.img,media=disk \
		  $$SECOND_DISK_ARGS \
		  -nic $$NIC \
		  -rtc base=localtime \
		  -m 256M \
		  -display $$DISPLAY_BACKEND \
		  -device $$VGA_DEVICE \
		  -audiodev sdl,id=snd0 \
		  -machine pcspk-audiodev=snd0 \
		  -global isa-debugcon.iobase=0x402 \
		  -boot order=d; \
	else \
		echo "No display detected; launching CopperOS in headless mode"; \
		$(QEMU) -drive format=raw,file=$(BUILD_DIR)/copperos.img,media=disk \
		  $$SECOND_DISK_ARGS \
		  -nic user,model=virtio-net-pci,mac=52:54:00:12:34:56 \
		  -rtc base=localtime \
		  -m 256M \
		  -display none \
		  -serial stdio \
		  -no-reboot -no-shutdown; \
	fi

run-headless: $(BUILD_DIR)/copperos.img
	@SECOND_DISK_ARGS=""; \
	if [ -n "$${COPPEROS_SECOND_DISK:-}" ]; then \
		SECOND_DISK_ARGS="-drive if=none,format=raw,file=$$COPPEROS_SECOND_DISK,id=seconddisk -device ide-hd,drive=seconddisk,bus=ide.1,unit=0"; \
	fi; \
	$(QEMU) -drive format=raw,file=$(BUILD_DIR)/copperos.img,media=disk $$SECOND_DISK_ARGS -nic user,model=virtio-net-pci,mac=52:54:00:12:34:56 -display none -serial stdio -no-reboot -no-shutdown

# --- UEFI boot path (build/BOOTX64.EFI) -- see boot/efi/efi_stub.c and
# the plan file's Part 1 for the full design/staged rollout. All three
# stages now: print status, locate + read the whole of KERNEL.BIN, query
# GOP and populate BootInfo, ExitBootServices, then
# boot/efi/efi_trampoline.asm's long-mode-to-protected-mode transition
# into CopperOS itself at the same fixed physical 0x10000 the BIOS path
# also jumps to.
$(BUILD_DIR)/efi:
	mkdir -p $(BUILD_DIR)/efi

$(BUILD_DIR)/efi/efi_stub.o: boot/efi/efi_stub.c boot/efi/efi_types.h kernel/include/bootinfo.h kernel/include/types.h | $(BUILD_DIR)/efi
	$(CC) $(EFICFLAGS) -Ikernel/include -c -o $(BUILD_DIR)/efi/efi_stub.o boot/efi/efi_stub.c

# Long-mode(64-bit)-to-32-bit-protected-mode trampoline (stage 3's final
# step, see boot/efi/efi_trampoline.asm's own file comment) -- kept as a
# separate hand-written NASM file rather than inline asm in efi_stub.c,
# matching how kernel/arch/kernel_entry.asm/idt.asm already isolate
# delicate low-level mode-transition code from C on the BIOS-path side.
# ELF64 (not elf32 like the kernel's own .asm files -- this trampoline
# starts executing in genuine 64-bit long mode, only becoming 32-bit code
# partway through its own body).
$(BUILD_DIR)/efi/efi_trampoline.o: boot/efi/efi_trampoline.asm | $(BUILD_DIR)/efi
	$(NASM) -f elf64 -o $(BUILD_DIR)/efi/efi_trampoline.o boot/efi/efi_trampoline.asm

# -shared -Bsymbolic (not a plain executable link): this is the standard
# gnu-efi-less recipe for getting ld to emit a PE32+-convertible image
# from this toolchain (no dedicated efi-app-x86_64 BFD target exists
# here to do it in one step, see the plan file). -e efi_main sets the
# entry point objcopy/the PE header will record. --image-base 0 (also
# passed to objcopy below, and must match) -- EFI loaders allocate pages
# and load a PE wherever they like regardless of its declared preferred
# base anyway (that mismatch is exactly what .reloc exists to handle,
# see fix_pe_reloc.py), so there's no reason to request a specific
# nonzero base ld would then have to actually place sections at.
#
# -T boot/efi/efi_link.ld is NOT optional/cosmetic: a plain `ld -shared`
# with no custom section layout packs ELF sections at whatever addresses
# its default script chooses (e.g. .dynsym at 0x278), which objcopy
# happily converts straight into the PE's VirtualAddress fields -- except
# the PE spec requires every section's VirtualAddress to be a multiple of
# SectionAlignment (0x1000). A build without this linker script produces
# a PE that LOOKS structurally valid under a header-only check (correct
# signatures/Machine/Subsystem/etc, which is how this went unnoticed
# initially) but real UEFI firmware's loader can and does reject it
# outright at load time -- before the entry point ever runs, which is
# exactly the "boot device failed, zero CopperOS output" failure this
# fixes. See efi_link.ld's own comment for what it merges and why.
$(BUILD_DIR)/efi/BOOTX64.so: $(BUILD_DIR)/efi/efi_stub.o $(BUILD_DIR)/efi/efi_trampoline.o boot/efi/efi_link.ld
	$(LD) -nostdlib -shared -Bsymbolic --image-base 0x0 -T boot/efi/efi_link.ld -e efi_main -o $(BUILD_DIR)/efi/BOOTX64.so $(BUILD_DIR)/efi/efi_stub.o $(BUILD_DIR)/efi/efi_trampoline.o

# objcopy's ELF->PE32+ conversion sets IMAGE_FILE_RELOCS_STRIPPED with
# an empty .reloc directory, which some firmware's loader rejects unless
# the image lands at its declared preferred base -- fix_pe_reloc.py
# patches that (see its own file comment for exactly why this is safe
# given efi_stub.c's no-absolute-relocations-needed coding rule).
# Only 4 sections are kept now that efi_link.ld merges everything else
# into .text/.rodata/.data/.bss (each page-aligned) -- .dynamic/.dynsym/
# .got.plt/etc no longer exist as independent section entries at all.
$(BUILD_DIR)/BOOTX64.EFI: $(BUILD_DIR)/efi/BOOTX64.so scripts/fix_pe_reloc.py
	$(OBJCOPY) -j .text -j .rodata -j .data -j .bss -j .reloc \
		-O pei-x86-64 --subsystem=10 --image-base=0x0 \
		$(BUILD_DIR)/efi/BOOTX64.so $(BUILD_DIR)/BOOTX64.EFI
	python3 scripts/fix_pe_reloc.py $(BUILD_DIR)/BOOTX64.EFI

efi: $(BUILD_DIR)/BOOTX64.EFI

# A small hand-authored FAT12 image (no mtools/mkfs.vfat available, see
# the plan file) containing /EFI/BOOT/BOOTX64.EFI and /KERNEL.BIN --
# this is what the ISO's *second*, UEFI-platform El Torito entry points
# firmware at (see scripts/build_iso.py); BIOS firmware never looks at
# it at all, only the existing hdemul entry.
$(BUILD_DIR)/efiboot.img: $(BUILD_DIR)/BOOTX64.EFI $(BUILD_DIR)/kernel.bin scripts/build_fat_image.py
	python3 scripts/build_fat_image.py $(BUILD_DIR)/BOOTX64.EFI $(BUILD_DIR)/kernel.bin $(BUILD_DIR)/efiboot.img

# Cross-platform (Linux/macOS/Windows, anywhere Python 3 + pycdlib run) --
# see scripts/build_iso.py for why the El Torito entry must use hard-disk
# emulation rather than no-emulation (for the existing BIOS entry) and
# efi=True rather than platform_id= (for the new UEFI entry).
$(BUILD_DIR)/copperos.iso: $(BUILD_DIR)/copperos.img $(BUILD_DIR)/efiboot.img
	rm -f $(BUILD_DIR)/copperos.iso
	python3 scripts/build_iso.py

iso: $(BUILD_DIR)/copperos.iso

# scripts/build_vdi.py already existed in this repo (hand-writes the VDI
# format, no VBoxManage/qemu-img dependency -- portable anywhere Python 3
# runs, matching build_iso.py/build_fat_image.py/build_usb_image.py's own
# philosophy), but this target was never wired to actually call it -- it
# just aliased `iso` and echoed a message that didn't even mention a
# .vdi. Fixed as of 1.6 Big Waterfall: this now really produces
# build/copperos.vdi.
$(BUILD_DIR)/copperos.vdi: $(BUILD_DIR)/copperos.img scripts/build_vdi.py
	python3 scripts/build_vdi.py

vbox: $(BUILD_DIR)/copperos.vdi
	@echo "VirtualBox disk image built at $(BUILD_DIR)/copperos.vdi -- attach it as a hard disk, not a CD."

# THE artifact for flashing to a real USB drive (BalenaEtcher, Rufus,
# `dd`, etc.) and booting on real hardware -- see scripts/
# build_usb_image.py for exactly why this must be a separate file from
# both copperos.img and copperos.iso: UEFI firmware booting from a raw
# USB block device never looks at El Torito/ISO9660 at all (that's an
# optical-disc-only mechanism), so copperos.iso flashed directly to USB
# only ever offers BIOS boot in practice, never "Boot from EFI file".
# This adds a real second MBR partition (type 0xEF, an EFI System
# Partition) alongside copperos.img's existing boot code, which
# pycdlib's hdemul validation would otherwise reject if done to
# copperos.img itself (see the script's own comment).
$(BUILD_DIR)/copperos-usb.img: $(BUILD_DIR)/copperos.img $(BUILD_DIR)/efiboot.img scripts/build_usb_image.py
	python3 scripts/build_usb_image.py $(BUILD_DIR)/copperos.img $(BUILD_DIR)/efiboot.img $(BUILD_DIR)/copperos-usb.img

usb-image: $(BUILD_DIR)/copperos-usb.img
	@echo "Flash this to a USB drive for real hardware (BIOS and UEFI both): $(BUILD_DIR)/copperos-usb.img"

clean:
	rm -rf $(BUILD_DIR)/*.bin $(BUILD_DIR)/*.img $(BUILD_DIR)/*.inc $(BUILD_DIR)/*.o $(BUILD_DIR)/*.elf $(BUILD_DIR)/*.map $(BUILD_DIR)/assets $(BUILD_DIR)/assets.stamp $(BUILD_DIR)/*.iso $(BUILD_DIR)/*.vdi $(BUILD_DIR)/apps $(BUILD_DIR)/net $(BUILD_DIR)/efi $(BUILD_DIR)/BOOTX64.EFI

