# Make Variables
#
# TARGET: Optional; Platform to build for.
#     - Supported values: NATIVE, NATIVE32, NATIVE64, WIN32, WIN64, MAC32, MAC64, LINUX32, LINUX64, 3DS, WIIU, SWITCH
#     - Default value: NATIVE
# LIBRARY: Optional; Whether to output a library.
#     - Supported values: 0, 1
#     - Default value: 0
#
# All:
# - NAME: Project name.
# - INCLUDE_DIRS: Directories containing include headers.
# - SOURCE_DIRS: Directories containing source files to compile.
# - BUILD_DIR: Directory to store build files in.
# - OUTPUT_DIR: Directory to output the final results to.
# - LIBRARY_DIRS: Optional; Directories containing libraries to compile against.
# - LIBRARIES: Optional; Libraries to compile against.
# - EXTRA_OUTPUT_FILES: Optional; Extra files to copy to the output directory.
# - BUILD_FLAGS: Optional; Shared build flags.
# - BUILD_FLAGS_CC: Optional; C build flags.
# - BUILD_FLAGS_CXX: Optional; C++ build flags.
# - RUN_FLAGS: Optional; Flags to pass when running output executables.
# - VERSION_MAJOR: Optional; Major version number.
#     - Default value: 0
# - VERSION_MINOR: Optional; Minor version number.
#     - Default value: 0
# - VERSION_MICRO: Optional; Micro version number.
#     - Default value: 0
#
# 3DS/Wii U/Switch:
# - TITLE: Optional; Formal application title, used in metadata.
#     - Default value: NAME stripped of spaces.
# - AUTHOR: Optional; Application author.
#     - Default value: "Unknown"
# - REMOTE_IP: Optional; IP to send executable to when running on hardware. Intended to be set in command line.
#     - Default value: 127.0.0.1
#
# 3DS/Wii U:
# - DESCRIPTION: Optional; Application description.
#     - Default value: "No description."
#
# 3DS:
# - PRODUCT_CODE: CIA/3DS product code.
# - UNIQUE_ID: CIA/3DS unique ID.
# - BANNER_AUDIO: Audio file to use in the CIA/3DS banner.
#     - Supported file types: WAV, CWAV
# - BANNER_IMAGE: Graphics to use in the CIA/3DS banner.
#     - Supported file types: 256x128 PNG, CGFX
# - ICON: Application icon.
#     - Supported file types: 48x48 PNG
# - Category: Optional; CIA/3DS category.
#     - Supported values: Application, SystemApplication, Applet, Firmware, Base, DlpChild, Demo, Contents, SystemContents, SharedContents, AddOnContents, Patch, AutoUpdateContents
#     - Default value: Application
# - USE_ON_SD: Optional; Whether the CIA/3DS should be installed to the SD card.
#     - Supported values: true, false
#     - Default value: true
# - MEMORY_TYPE: Optional; CIA/3DS application memory layout.
#     - Supported values: Application, System, Base
#     - Default value: Application
# - SYSTEM_MODE: Optional; CIA/3DS legacy system mode.
#     - Supported values: 32MB, 64MB, 72MB, 80MB, 96MB
#     - Default value: 64MB
# - SYSTEM_MODE_EXT: Optional; CIA/3DS extended system mode.
#     - Supported values: Legacy, 124MB, 178MB
#     - Default value: Legacy
# - CPU_MODE: Optional; CIA/3DS CPU frequency. 804MHz is N3DS-only.
#     - Supported values: 268MHz, 804MHz
#     - Default value: 268MHz
# - ENABLE_L2_CACHE: Optional; Whether the CIA/3DS should use the N3DS L2 cache.
#     - Supported values: true, false
#     - Default value: false
# - ICON_FLAGS: Optional; Flags to pass to bannertool when making an SMDH icon.
# - ROMFS_DIR: Optional; Directory containing RomFS files.
# - LOGO: Optional; Logo animation to use when launching the CIA/3DS.
#     - Supported file types: BCMA.LZ
#
# Wii U:
# - ICON: Application icon.
#     - Supported file types: 256x96 PNG
# - LONG_DESCRIPTION: Optional; Long version of the description field.
#     - Default value: Value of DESCRIPTION.
#
# Switch:
# - TITLE_ID: Optional; Application title ID.
# - ICON: Optional; Application icon.
#     - Supported file types: 256x256 JPEG

# PROLOGUE #

TARGET ?= NATIVE
LIBRARY ?= 0

ALL_PC_TARGETS := WIN32 WIN64 MAC32 MAC64 LINUX32 LINUX64
ALL_SPECIFIC_TARGETS := $(ALL_PC_TARGETS) 3DS WIIU SWITCH
ALL_TARGETS := NATIVE NATIVE32 NATIVE64 $(ALL_SPECIFIC_TARGETS)

TARGETS :=

ifneq (1,$(words $(TARGET)))
    TARGETS := $(TARGET)
else ifeq ($(TARGET),ALL)
    TARGETS := $(ALL_SPECIFIC_TARGETS)
else ifeq ($(TARGET),PC)
    TARGETS := $(ALL_PC_TARGETS)
endif

ifneq ($(TARGETS),)

.PHONY: all clean
all:
	@$(foreach target,$(TARGETS),make --no-print-directory TARGET=$(target);)

else

ifneq ($(MAKECMDGOALS),clean)
    $(info Building for $(TARGET)...)
endif

ifeq ($(TARGET),$(filter $(TARGET),3DS WIIU SWITCH))
    ifeq ($(strip $(DEVKITPRO)),)
        $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>devkitPro")
    endif
endif

# TOOLS #

BUILDTOOLS_DIR := $(dir $(lastword $(MAKEFILE_LIST)))

define createdirrule
$(1): | $(dir $(1))

ifndef $(dir $(1))_DIRECTORY_RULE_IS_DEFINED
$(dir $(1)):
	@mkdir -p $$@

$(dir $(1))_DIRECTORY_RULE_IS_DEFINED := 1
endif
endef

rwildcard=$(wildcard $1/$2) $(foreach d,$(wildcard $1/*),$(call rwildcard,$d,$2))

# INITIAL COMMON SETUP #

EMPTY :=
SPACE := $(EMPTY) $(EMPTY)
STRIPPED_NAME := $(subst $(SPACE),,$(NAME))

ifeq ($(OS),Windows_NT)
    HOST_OS := windows
    ifeq ($(PROCESSOR_ARCHITECTURE),AMD64)
        HOST_ARCH := x86_64
    else
        HOST_ARCH := i686
    endif
else
    UNAME_S := $(shell uname -s)
    ifeq ($(UNAME_S),Darwin)
        HOST_OS := mac
    else ifeq ($(UNAME_S),Linux)
        HOST_OS := linux
    else
        $(error "Unsupported host OS.")
    endif

    UNAME_M := $(shell uname -m)
    ifeq ($(UNAME_M),$(filter $(UNAME_M),x86_64 amd64))
        HOST_ARCH := x86_64
    else ifeq ($(UNAME_M),$(filter $(UNAME_M),i386 i686))
        HOST_ARCH := i686
    else
        $(error "Unsupported host architecture.")
    endif
endif

ifeq ($(TARGET),NATIVE)
    TARGET_OS := $(HOST_OS)
    TARGET_ARCH := $(HOST_ARCH)
else ifeq ($(TARGET),NATIVE32)
    TARGET_OS := $(HOST_OS)
    TARGET_ARCH := i686
else ifeq ($(TARGET),NATIVE64)
    TARGET_OS := $(HOST_OS)
    TARGET_ARCH := x86_64
else ifeq ($(TARGET),WIN32)
    TARGET_OS := windows
    TARGET_ARCH := i686
else ifeq ($(TARGET),WIN64)
    TARGET_OS := windows
    TARGET_ARCH := x86_64
else ifeq ($(TARGET),LINUX32)
    TARGET_OS := linux
    TARGET_ARCH := i686
else ifeq ($(TARGET),LINUX64)
    TARGET_OS := linux
    TARGET_ARCH := x86_64
else ifeq ($(TARGET),MAC32)
    TARGET_OS := mac
    TARGET_ARCH := i686
else ifeq ($(TARGET),MAC64)
    TARGET_OS := mac
    TARGET_ARCH := x86_64
else ifeq ($(TARGET),3DS)
    TARGET_OS := 3ds
    TARGET_ARCH := arm
else ifeq ($(TARGET),WIIU)
    TARGET_OS := wiiu
    TARGET_ARCH := ppc
else ifeq ($(TARGET),SWITCH)
    TARGET_OS := switch
    TARGET_ARCH := aarch64
else
    $(error "Unknown target. Supported targets: $(ALL_TARGETS)")
endif

TARGET_BUILD_DIR := $(BUILD_DIR)/$(TARGET_OS)-$(TARGET_ARCH)
TARGET_OUTPUT_DIR := $(OUTPUT_DIR)/$(TARGET_OS)-$(TARGET_ARCH)

BUILT_FILTER := $(patsubst %.bin,$(TARGET_BUILD_DIR)/%.bin.o,$(BUILD_FILTER)) \
		$(patsubst %.c,$(TARGET_BUILD_DIR)/%.o,$(BUILD_FILTER)) \
		$(patsubst %.cpp,$(TARGET_BUILD_DIR)/%.o,$(BUILD_FILTER)) \
		$(patsubst %.s,$(TARGET_BUILD_DIR)/%.o,$(BUILD_FILTER))

OBJECT_FILES := $(foreach dir,$(SOURCE_DIRS), \
			$(patsubst %.bin,$(TARGET_BUILD_DIR)/%.bin.o,$(call rwildcard,$(dir),*.bin)) \
			$(patsubst %.c,$(TARGET_BUILD_DIR)/%.o,$(call rwildcard,$(dir),*.c)) \
			$(patsubst %.cpp,$(TARGET_BUILD_DIR)/%.o,$(call rwildcard,$(dir),*.cpp)) \
			$(patsubst %.s,$(TARGET_BUILD_DIR)/%.o,$(call rwildcard,$(dir),*.s)) \
		)

OBJECT_FILES := $(filter-out $(BUILT_FILTER),$(OBJECT_FILES))

OUTPUT_ZIP_FILE ?= $(OUTPUT_DIR)/$(STRIPPED_NAME).zip

ifeq ($(strip $(VERSION_MAJOR)),)
    VERSION_MAJOR := 0
endif

ifeq ($(strip $(VERSION_MINOR)),)
    VERSION_MINOR := 0
endif

ifeq ($(strip $(VERSION_MICRO)),)
    VERSION_MICRO := 0
endif

LD_FLAGS := $(patsubst %,-L%/lib,$(LIBRARY_DIRS)) $(patsubst %,-l%,$(LIBRARIES))
COMMON_CC_FLAGS := $(sort $(foreach dir,$(SOURCE_DIRS),$(patsubst %,-I$(TARGET_BUILD_DIR)/%,$(dir $(call rwildcard,$(dir),*))))) $(patsubst %,-I%,$(INCLUDE_DIRS)) $(patsubst %,-I%/include,$(LIBRARY_DIRS)) -g -Wall -DVERSION_MAJOR=$(VERSION_MAJOR) -DVERSION_MINOR=$(VERSION_MINOR) -DVERSION_MICRO=$(VERSION_MICRO) $(BUILD_FLAGS)
COMMON_CXX_FLAGS :=

ifeq ($(findstring -O,$(BUILD_FLAGS)),)
	COMMON_CC_FLAGS += -O2
endif

# COMMON LIBRARY SETUP #

ifeq ($(LIBRARY),1)
    STRIPPED_NAME := lib$(STRIPPED_NAME)
    EXTRA_OUTPUT_FILES += $(INCLUDE_DIRS)
endif

# TARGET SETUP #

REMOTE_IP ?= 127.0.0.1 # User-defined

TITLE ?= $(NAME)
AUTHOR ?= "Unknown"
DESCRIPTION ?= "No description."
LONG_DESCRIPTION ?= $(DESCRIPTION)

ifeq ($(TARGET_OS),windows)
    ifeq ($(HOST_OS),windows)
        AR := ar
        AS := as
        CC := gcc
        CXX := g++
    else ifeq ($(TARGET_ARCH),i686)
        AR := i686-w64-mingw32-ar
        AS := i686-w64-mingw32-as
        CC := i686-w64-mingw32-gcc
        CXX := i686-w64-mingw32-g++
    else ifeq ($(TARGET_ARCH),x86_64)
        AR := x86_64-w64-mingw32-ar
        AS := x86_64-w64-mingw32-as
        CC := x86_64-w64-mingw32-gcc
        CXX := x86_64-w64-mingw32-g++
    endif

    ifeq ($(TARGET_ARCH),i686)
        COMMON_CC_FLAGS += -m32
    else ifeq ($(TARGET_ARCH),x86_64)
        COMMON_CC_FLAGS += -m64
    endif

    LD_FLAGS += -static-libstdc++ -static-libgcc -static

    ifeq ($(LIBRARY),1)
        OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).a $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).dll
        COMMON_CC_FLAGS += -fPIC
    else
        OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/$(STRIPPED_NAME).exe
    endif
else ifeq ($(TARGET_OS),mac)
    ifeq ($(HOST_OS),mac)
        AR := ar
        AS := as
        CC := gcc
        CXX := g++
    else ifeq ($(TARGET_ARCH),i686)
        AR := i386-apple-darwin15-ar
        AS := i386-apple-darwin15-as
        CC := i386-apple-darwin15-gcc
        CXX := i386-apple-darwin15-g++
    else ifeq ($(TARGET_ARCH),x86_64)
        AR := x86_64-apple-darwin15-ar
        AS := x86_64-apple-darwin15-as
        CC := x86_64-apple-darwin15-gcc
        CXX := x86_64-apple-darwin15-g++
    endif

    ifeq ($(TARGET_ARCH),i686)
        COMMON_CC_FLAGS += -m32
    else ifeq ($(TARGET_ARCH),x86_64)
        COMMON_CC_FLAGS += -m64
    endif

    ifeq ($(LIBRARY),1)
        OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).a $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).so $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).dylib
        COMMON_CC_FLAGS += -fPIC
    else
        OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/$(STRIPPED_NAME)
    endif
else ifeq ($(TARGET_OS),linux)
    ifeq ($(HOST_OS),linux)
        AR := ar
        AS := as
        CC := gcc
        CXX := g++
    else ifeq ($(TARGET_ARCH),i686)
        AR := i686-pc-linux-gnu-ar
        AS := i686-pc-linux-gnu-as
        CC := i686-pc-linux-gnu-gcc
        CXX := i686-pc-linux-gnu-g++
    else ifeq ($(TARGET_ARCH),x86_64)
        AR := x86_64-pc-linux-gnu-ar
        AS := x86_64-pc-linux-gnu-as
        CC := x86_64-pc-linux-gnu-gcc
        CXX := x86_64-pc-linux-gnu-g++
    endif

    ifeq ($(TARGET_ARCH),i686)
        COMMON_CC_FLAGS += -m32
    else ifeq ($(TARGET_ARCH),x86_64)
        COMMON_CC_FLAGS += -m64
    endif

    ifeq ($(LIBRARY),1)
        OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).a $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).so
        COMMON_CC_FLAGS += -fPIC
    else
        OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/$(STRIPPED_NAME)
    endif
else ifeq ($(TARGET_OS),3ds)
    BUILT_FILTER := $(patsubst %.v.pica,$(TARGET_BUILD_DIR)/%.shbin.o,$(BUILD_FILTER)) \
		$(patsubst %.shlist,$(TARGET_BUILD_DIR)/%.shbin.o,$(BUILD_FILTER)) \

    OBJECT_FILES := $(foreach dir,$(SOURCE_DIRS), \
			$(patsubst %.v.pica,$(TARGET_BUILD_DIR)/%.shbin.o,$(call rwildcard,$(dir),*.v.pica)) \
			$(patsubst %.shlist,$(TARGET_BUILD_DIR)/%.shbin.o,$(call rwildcard,$(dir),*.shlist)) \
		) $(OBJECT_FILES)

    OBJECT_FILES := $(filter-out $(BUILT_FILTER),$(OBJECT_FILES))

    DEVKITARM := $(DEVKITPRO)/devkitARM

    AR := $(DEVKITARM)/bin/arm-none-eabi-ar
    AS := $(DEVKITARM)/bin/arm-none-eabi-as
    CC := $(DEVKITARM)/bin/arm-none-eabi-gcc
    CXX := $(DEVKITARM)/bin/arm-none-eabi-g++

    ifeq ($(LIBRARY),1)
        OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).a
    else
        OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/$(STRIPPED_NAME).elf $(TARGET_OUTPUT_DIR)/3ds/$(STRIPPED_NAME)/$(STRIPPED_NAME).3dsx $(TARGET_OUTPUT_DIR)/$(STRIPPED_NAME).3ds $(TARGET_OUTPUT_DIR)/$(STRIPPED_NAME).cia
    endif

    LD_FLAGS += -specs=3dsx.specs
    COMMON_CC_FLAGS += -mword-relocations -ffast-math -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft -DARM11 -D_3DS

    SERVEFILES := python $(BUILDTOOLS_DIR)/3ds/servefiles.py

    CATEGORY ?= Application
    USE_ON_SD ?= true
    MEMORY_TYPE ?= Application
    SYSTEM_MODE ?= 64MB
    SYSTEM_MODE_EXT ?= Legacy
    CPU_SPEED ?= 268MHz
    ENABLE_L2_CACHE ?= false

    _3DSXTOOL_FLAGS :=
    COMMON_MAKEROM_FLAGS := -rsf $(BUILDTOOLS_DIR)/3ds/template.rsf -target t -exefslogo -icon $(TARGET_BUILD_DIR)/icon.icn -banner $(TARGET_BUILD_DIR)/banner.bnr -major $(VERSION_MAJOR) -minor $(VERSION_MINOR) -micro $(VERSION_MICRO) -DAPP_TITLE="$(TITLE)" -DAPP_PRODUCT_CODE="$(PRODUCT_CODE)" -DAPP_UNIQUE_ID="$(UNIQUE_ID)" -DAPP_SYSTEM_MODE="$(SYSTEM_MODE)" -DAPP_SYSTEM_MODE_EXT="$(SYSTEM_MODE_EXT)" -DAPP_CATEGORY="$(CATEGORY)" -DAPP_USE_ON_SD="$(USE_ON_SD)" -DAPP_MEMORY_TYPE="$(MEMORY_TYPE)" -DAPP_CPU_SPEED="$(CPU_SPEED)" -DAPP_ENABLE_L2_CACHE="$(ENABLE_L2_CACHE)" -DAPP_VERSION_MAJOR="$(VERSION_MAJOR)"

    ifneq ("$(wildcard $(ROMFS_DIR))","")
        _3DSXTOOL_FLAGS += --romfs=$(ROMFS_DIR)
        COMMON_MAKEROM_FLAGS += -DAPP_ROMFS="$(ROMFS_DIR)"
    endif

    ifneq ("$(wildcard $(LOGO))","")
        COMMON_MAKEROM_FLAGS += -logo "$(LOGO)"
    else ifneq ($(LOGO),plain)
        COMMON_MAKEROM_FLAGS += -logo "$(BUILDTOOLS_DIR)/3ds/logo.bcma.lz"
    endif

    ifeq ($(suffix $(BANNER_IMAGE)),.cgfx)
        BANNER_IMAGE_ARG := -ci
    else
        BANNER_IMAGE_ARG := -i
    endif

    ifeq ($(suffix $(BANNER_AUDIO)),.cwav)
        BANNER_AUDIO_ARG := -ca
    else
        BANNER_AUDIO_ARG := -a
    endif
else ifeq ($(TARGET_OS),wiiu)
    DEVKITPPC := $(DEVKITPRO)/devkitPPC

    AR := $(DEVKITPPC)/bin/powerpc-eabi-ar
    AS := $(DEVKITPPC)/bin/powerpc-eabi-as
    CC := $(DEVKITPPC)/bin/powerpc-eabi-gcc
    CXX := $(DEVKITPPC)/bin/powerpc-eabi-g++

    ifeq ($(LIBRARY),1)
        OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).a
    else
        OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/$(STRIPPED_NAME).elf $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/meta.xml $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/icon.png
    endif

    LD_FLAGS += -Wl,-d,--gc-sections
    COMMON_CC_FLAGS += -mwup -mcpu=750 -meabi -mhard-float -ffast-math -DESPRESSO -DWIIU
else ifeq ($(TARGET_OS),switch)
    DEVKITA64 := $(DEVKITPRO)/devkitA64

    AR := $(DEVKITA64)/bin/aarch64-none-elf-ar
    AS := $(DEVKITA64)/bin/aarch64-none-elf-as
    CC := $(DEVKITA64)/bin/aarch64-none-elf-gcc
    CXX := $(DEVKITA64)/bin/aarch64-none-elf-g++

    ifeq ($(LIBRARY),1)
        OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/lib/$(STRIPPED_NAME).a
    else
        OUTPUT_FILES := $(TARGET_OUTPUT_DIR)/$(STRIPPED_NAME).elf $(TARGET_OUTPUT_DIR)/switch/$(STRIPPED_NAME)/$(STRIPPED_NAME).nro
    endif

    LD_FLAGS += -specs=$(DEVKITPRO)/libnx/switch.specs
    COMMON_CC_FLAGS += -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE -D__SWITCH__
    COMMON_CXX_FLAGS += -fno-rtti -fno-exceptions

    NRO_FLAGS :=

    ifneq ("$(wildcard $(ICON))","")
        NRO_FLAGS += --icon=$(ICON)
    endif

    NACP_FLAGS :=

    ifneq ($(TITLE_ID),)
        NACP_FLAGS += --titleid=$(TITLE_ID)
    endif
endif

# FINAL COMMON SETUP #

CLANG_CC := $(subst gcc,clang,$(CC))
CLANG_CC_EXISTS := $(shell which $(CLANG_CC) > /dev/null 2> /dev/null; echo $$?)
ifeq ($(CLANG_CC_EXISTS),0)
    CC := $(CLANG_CC)
endif

CLANG_CXX := $(subst g++,clang++,$(CXX))
CLANG_CXX_EXISTS := $(shell which $(CLANG_CXX) > /dev/null 2> /dev/null; echo $$?)
ifeq ($(CLANG_CXX_EXISTS),0)
    CXX := $(CLANG_CXX)
endif

CC_FLAGS := $(COMMON_CC_FLAGS) $(BUILD_FLAGS_CC)
CXX_FLAGS := $(COMMON_CC_FLAGS) $(COMMON_CXX_FLAGS) $(BUILD_FLAGS_CXX)

ifeq ($(findstring -std,$(BUILD_FLAGS_CC)),)
	CC_FLAGS += -std=gnu11
endif

ifeq ($(findstring -std,$(BUILD_FLAGS_CXX)),)
	CXX_FLAGS += -std=gnu++11
endif

ifneq ($(EXTRA_OUTPUT_FILES),)
    EXTRA_OUTPUT_COPY_CMD := cp -r $(EXTRA_OUTPUT_FILES) $(OUTPUT_DIR)
endif

# MAIN RULES #

.PHONY: all run install clean

all: $(OUTPUT_ZIP_FILE)

# TARGET RULES #

ifeq ($(TARGET_OS),3ds)

ifeq ($(LIBRARY),1)

install: $(OUTPUT_ZIP_FILE)
	@mkdir -p $(DEVKITPRO)/$(STRIPPED_NAME)
	@cp -r $(TARGET_OUTPUT_DIR)/* $(DEVKITPRO)/$(STRIPPED_NAME)
	@echo "Installed."

else

run: $(OUTPUT_ZIP_FILE)
	@echo "Running..."
	@citra $(RUN_FLAGS) $(TARGET_OUTPUT_DIR)/3ds/$(STRIPPED_NAME)/$(STRIPPED_NAME).3dsx

runhw: $(OUTPUT_ZIP_FILE)
	@echo "Running..."
	@3dslink --address $(REMOTE_IP) $(TARGET_OUTPUT_DIR)/3ds/$(STRIPPED_NAME)/$(STRIPPED_NAME).3dsx

install: $(OUTPUT_ZIP_FILE)
	@echo "Installing..."
	@$(SERVEFILES) $(REMOTE_IP) $(TARGET_OUTPUT_DIR)/$(STRIPPED_NAME).cia
	@echo "Installed."

endif

$(TARGET_BUILD_DIR)/%.shbin.o: $(TARGET_BUILD_DIR)/%.shbin.c
	@echo $@
	@$(CC) -c $(CC_FLAGS) -MMD -MP -MF $(TARGET_BUILD_DIR)/$*.d $< -o $@

define shader-as
	$(eval CURBIN := $(patsubst %.shbin.c,%.shbin,$@))
	@picasso -o $(CURBIN) $1
	@cd $(dir $(CURBIN)); \
	xxd -i $(notdir $(CURBIN)) "$(CURDIR)/$@"
	echo "extern const u8" `(echo $(notdir $(CURBIN)) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h
	echo "extern const u32" `(echo $(notdir $(CURBIN)) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_len";" >> `(echo $(CURBIN) | tr . _)`.h
endef

$(TARGET_BUILD_DIR)/%.shbin.c: %.v.pica %.g.pica
	@echo $@
	@$(call shader-as,$^)

$(TARGET_BUILD_DIR)/%.shbin.c: %.v.pica
	@echo $@
	@$(call shader-as,$<)

$(TARGET_BUILD_DIR)/%.shbin.c: %.shlist
	@echo $@
	@$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file)))

%.bnr: $(BANNER_IMAGE) $(BANNER_AUDIO)
	@echo $@
	@bannertool makebanner $(BANNER_IMAGE_ARG) $(BANNER_IMAGE) $(BANNER_AUDIO_ARG) $(BANNER_AUDIO) -o $@ > /dev/null

%.icn: $(ICON)
	@echo $@
	@bannertool makesmdh -s "$(TITLE)" -l "$(TITLE) - $(DESCRIPTION)" -p "$(AUTHOR)" -i $(ICON) $(ICON_FLAGS) -o $@ > /dev/null

%.smdh: $(ICON)
	@echo $@
	@smdhtool --create "$(TITLE)" "$(DESCRIPTION)" "$(AUTHOR)" $(ICON) $@

$(TARGET_OUTPUT_DIR)/3ds/$(STRIPPED_NAME)/%.3dsx: $(TARGET_OUTPUT_DIR)/%.elf $(TARGET_BUILD_DIR)/meta.smdh
	@echo $@
	@3dsxtool $< $@ --smdh=$(word 2,$^) $(_3DSXTOOL_FLAGS)

%.3ds: %.elf $(TARGET_BUILD_DIR)/banner.bnr $(TARGET_BUILD_DIR)/icon.icn
	@echo $@
	@makerom -f cci -o $@ -elf $< -DAPP_ENCRYPTED=true $(COMMON_MAKEROM_FLAGS)

%.cia: %.elf $(TARGET_BUILD_DIR)/banner.bnr $(TARGET_BUILD_DIR)/icon.icn
	@echo $@
	@makerom -f cia -o $@ -elf $< -DAPP_ENCRYPTED=false $(COMMON_MAKEROM_FLAGS)

else ifeq ($(TARGET_OS),wiiu)

ifeq ($(LIBRARY),1)

install: $(OUTPUT_ZIP_FILE)
	@mkdir -p $(DEVKITPRO)/$(STRIPPED_NAME)
	@cp -r $(TARGET_OUTPUT_DIR)/* $(DEVKITPRO)/$(STRIPPED_NAME)
	@echo "Installed."

else

runhw: $(OUTPUT_ZIP_FILE)
	@echo "Running..."
	@WIILOAD=tcp:$(REMOTE_IP) wiiload $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/$(STRIPPED_NAME).elf $(RUN_FLAGS)

endif

$(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/meta.xml:
	@echo $@
	@cp $(BUILDTOOLS_DIR)/wiiu/meta_template.xml $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/meta.xml
	@sed -i -- 's/$$(TITLE)/$(subst /,\/,$(TITLE))/g' $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/meta.xml
	@sed -i -- 's/$$(AUTHOR)/$(subst /,\/,$(AUTHOR))/g' $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/meta.xml
	@sed -i -- 's/$$(VERSION)/$(subst /,\/,$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_MICRO))/g' $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/meta.xml
	@sed -i -- 's/$$(RELEASE_DATE)/$(subst /,\/,$(shell date +'%Y%m%d%H%M%S'))/g' $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/meta.xml
	@sed -i -- 's/$$(SHORT_DESCRIPTION)/$(subst /,\/,$(DESCRIPTION))/g' $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/meta.xml
	@sed -i -- 's/$$(LONG_DESCRIPTION)/$(subst /,\/,$(LONG_DESCRIPTION))/g' $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/meta.xml

$(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/icon.png:
	@echo $@
	@cp $(ICON) $(TARGET_OUTPUT_DIR)/wiiu/apps/$(STRIPPED_NAME)/icon.png

else ifeq ($(TARGET_OS),switch)

ifeq ($(LIBRARY),1)

install: $(OUTPUT_ZIP_FILE)
	@mkdir -p $(DEVKITPRO)/$(STRIPPED_NAME)
	@cp -r $(TARGET_OUTPUT_DIR)/* $(DEVKITPRO)/$(STRIPPED_NAME)
	@echo "Installed."

else

run: $(OUTPUT_ZIP_FILE)
	@echo "Running..."
	@yuzu-cmd $(RUN_FLAGS) $(TARGET_OUTPUT_DIR)/switch/$(STRIPPED_NAME)/$(STRIPPED_NAME).nro

runhw: $(OUTPUT_ZIP_FILE)
	@echo "Running..."
	@nxlink --address $(REMOTE_IP) $(TARGET_OUTPUT_DIR)/switch/$(STRIPPED_NAME)/$(STRIPPED_NAME).nro

endif

%.nacp:
	@echo $@
	@nacptool --create "$(TITLE)" "$(AUTHOR)" "$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_MICRO)" $@ $(NACP_FLAGS)

$(TARGET_OUTPUT_DIR)/switch/$(STRIPPED_NAME)/%.nro: $(TARGET_OUTPUT_DIR)/%.elf $(TARGET_BUILD_DIR)/meta.nacp
	@echo $@
	@elf2nro $< $@ --nacp=$(word 2,$^) $(NRO_FLAGS)

else ifeq ($(TARGET_OS),windows)
    ifeq ($(HOST_OS),$(filter $(HOST_OS),linux mac))
        ifneq ($(LIBRARY),1)

run: $(OUTPUT_FILES) $(OUTPUT_ZIP_FILE)
	@echo "Running..."
	@wine ./$< $(RUN_FLAGS)

        endif
    else ifeq ($(HOST_OS),windows)
        ifneq ($(LIBRARY),1)

run: $(OUTPUT_FILES) $(OUTPUT_ZIP_FILE)
	@echo "Running..."
	@./$< $(RUN_FLAGS)

        endif
    endif
else ifeq ($(TARGET_OS),$(filter $(TARGET_OS),mac linux))
    ifeq ($(HOST_OS),$(TARGET_OS))
        ifeq ($(LIBRARY),1)

install: $(OUTPUT_ZIP_FILE)
	@install -m 0755 $(OUTPUT_FILES) /usr/local/lib
	@install -m 0644 $(foreach dir,$(INCLUDE_DIRS),$(wildcard $(dir)/*)) /usr/local/include
	@echo "Installed."

        else

run: $(OUTPUT_FILES) $(OUTPUT_ZIP_FILE)
	@echo "Running..."
	@./$< $(RUN_FLAGS)

install: $(OUTPUT_ZIP_FILE)
	@install -m 0755 $(OUTPUT_FILES) /usr/local/bin
	@echo "Installed."

        endif
    endif
endif

# COMMON RULES #

$(OUTPUT_ZIP_FILE): $(OUTPUT_FILES) $(EXTRA_OUTPUT_FILES)
	@echo $@
	@$(EXTRA_OUTPUT_COPY_CMD)
	@cd $(OUTPUT_DIR); \
	zip -r $(patsubst $(OUTPUT_DIR)/%,%,$@ * -x $(OUTPUT_ZIP_FILE)) > /dev/null

$(TARGET_OUTPUT_DIR)/$(STRIPPED_NAME): $(OBJECT_FILES)
	@echo $@
	@$(CXX) $(CXX_FLAGS) $^ -o $@ $(LD_FLAGS)

%.elf: $(OBJECT_FILES)
	@echo $@
	@$(CXX) $(CXX_FLAGS) $^ -o $@ $(LD_FLAGS)

%.a: $(OBJECT_FILES)
	@echo $@
	@$(AR) -rc $@ $^

%.so: $(OBJECT_FILES)
	@echo $@
	@$(CXX) $(CXX_FLAGS) -shared $^ -o $@ $(LD_FLAGS)

%.exe: $(OBJECT_FILES)
	@echo $@
	@$(CXX) $(CXX_FLAGS) $^ -o $@ $(LD_FLAGS)

%.dll: $(OBJECT_FILES)
	@echo $@
	@$(CXX) $(CXX_FLAGS) -shared $^ -o $@ $(LD_FLAGS)

%.dylib: $(OBJECT_FILES)
	@echo $@
	@$(CXX) $(CXX_FLAGS) -dynamiclib -undefined suppress -flat_namespace $^ -o $@ $(LD_FLAGS)

$(TARGET_BUILD_DIR)/%.o: %.c
	@echo $@
	@$(CC) -c $(CC_FLAGS) -MMD -MP -MF $(TARGET_BUILD_DIR)/$*.d $< -o $@

$(TARGET_BUILD_DIR)/%.o: %.cpp
	@echo $@
	@$(CXX) -c $(CXX_FLAGS) -MMD -MP -MF $(TARGET_BUILD_DIR)/$*.d $< -o $@

$(TARGET_BUILD_DIR)/%.o: %.s
	@echo $@
	@$(CC) -c $(CC_FLAGS) -MMD -MP -MF $(TARGET_BUILD_DIR)/$*.d -x assembler-with-cpp $< -o $@

$(TARGET_BUILD_DIR)/%.o: %.S
	@echo $@
	@$(CC) -c $(CC_FLAGS) -MMD -MP -MF $(TARGET_BUILD_DIR)/$*.d -x assembler-with-cpp $< -o $@

$(TARGET_BUILD_DIR)/%.bin.o: $(TARGET_BUILD_DIR)/%.bin.c
	@echo $@
	@$(CC) -c $(CC_FLAGS) -MMD -MP -MF $(TARGET_BUILD_DIR)/$*.d $< -o $@

$(TARGET_BUILD_DIR)/%.bin.c: %.bin
	@echo $@
	@cd $(<D); \
	xxd -i $(<F) "$(CURDIR)/$@"
	@echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(TARGET_BUILD_DIR)/$< | tr . _)`.h
	@echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_len";" >> `(echo $(TARGET_BUILD_DIR)/$< | tr . _)`.h

$(foreach file,$(OBJECT_FILES),$(eval $(call createdirrule,$(file))))
$(foreach file,$(OUTPUT_FILES),$(eval $(call createdirrule,$(file))))

# DEPENDS #

DEPENDS	:= $(OBJECT_FILES:.o=.d)
-include $(DEPENDS)

endif

# CLEAN #

clean:
	@rm -rf $(BUILD_DIR) $(OUTPUT_DIR)
	@echo "Cleaned."
