@ -18,13 +18,35 @@ COMPARE ?= 1
# If NON_MATCHING is 1, define the NON_MATCHING and AVOID_UB macros when building (recommended)
NON_MATCHING ?= 0
# Build for the N64 (turn this off for ports)
TARGET_N64 ?= 1
TARGET_N64 ?= 0
# Compiler to use (ido or gcc)
COMPILER ?= ido
i f e q ( $( COMPILER ) , g c c )
NON_MATCHING := 1
e n d i f
# Build for Emscripten/WebGL
TARGET_WEB ?= 0
# Specify the target you are building for, 0 means native
TARGET_ARCH ?= native
TARGET_BITS ?= 0
i f n e q ( $( TARGET_BITS ) , 0 )
BITS := -m$( TARGET_BITS)
e l s e
BITS :=
e n d i f
# Automatic settings only for ports
i f e q ( $( TARGET_N 64) , 0 )
NON_MATCHING := 1
GRUCODE := f3dex2e
WINDOWS_BUILD := 0
ifeq ( $( TARGET_WEB) ,0)
ifeq ( $( OS) ,Windows_NT)
WINDOWS_BUILD := 1
endif
endif
# Release
@ -79,6 +101,11 @@ ifeq ($(GRUCODE), f3dex2) # Fast3DEX2
TARGET := $( TARGET) .f3dex2
COMPARE := 0
e l s e
i f e q ( $( GRUCODE ) , f 3 d e x 2 e ) # Fast3DEX2 Extended (for PC)
GRUCODE_CFLAGS := -DF3DEX_GBI_2E
TARGET := $( TARGET) .f3dex2e
COMPARE := 0
e l s e
i f e q ( $( GRUCODE ) , f 3 d _ n e w ) # Fast3D 2.0H (Shindou)
GRUCODE_CFLAGS := -DF3D_NEW
GRUCODE_ASFLAGS := --defsym F3D_NEW = 1
@ -95,6 +122,7 @@ endif
e n d i f
e n d i f
e n d i f
e n d i f
i f e q ( $( TARGET_N 64) , 0 )
NON_MATCHING := 1
@ -106,6 +134,10 @@ ifeq ($(NON_MATCHING),1)
COMPARE := 0
e n d i f
i f e q ( $( TARGET_WEB ) , 1 )
VERSION_CFLAGS := $( VERSION_CFLAGS) -DTARGET_WEB
e n d i f
################### Universal Dependencies ###################
# (This is a bit hacky, but a lot of rules implicitly depend
@ -137,9 +169,26 @@ endif
# BUILD_DIR is location where all build artifacts are placed
BUILD_DIR_BASE := build
BUILD_DIR := $( BUILD_DIR_BASE) /$( VERSION)
i f e q ( $( TARGET_N 64) , 1 )
BUILD_DIR := $( BUILD_DIR_BASE) /$( VERSION)
e l s e
i f e q ( $( TARGET_WEB ) , 1 )
BUILD_DIR := $( BUILD_DIR_BASE) /$( VERSION) _web
e l s e
BUILD_DIR := $( BUILD_DIR_BASE) /$( VERSION) _pc
e n d i f
e n d i f
LIBULTRA := $( BUILD_DIR) /libultra.a
i f e q ( $( TARGET_WEB ) , 1 )
EXE := $( BUILD_DIR) /$( TARGET) .html
e l s e
i f e q ( $( WINDOWS_BUILD ) , 1 )
EXE := $( BUILD_DIR) /$( TARGET) .exe
e l s e
EXE := $( BUILD_DIR) /$( TARGET)
e n d i f
e n d i f
ROM := $( BUILD_DIR) /$( TARGET) .z64
ELF := $( BUILD_DIR) /$( TARGET) .elf
LD_SCRIPT := sm64.ld
@ -151,7 +200,13 @@ LEVEL_DIRS := $(patsubst levels/%,%,$(dir $(wildcard levels/*/header.h)))
# Directories containing source files
SRC_DIRS := src src/engine src/game src/audio src/menu src/buffers actors levels bin data assets
ASM_DIRS := asm lib
ASM_DIRS := lib
i f e q ( $( TARGET_N 64) , 1 )
ASM_DIRS := asm $( ASM_DIRS)
e l s e
SRC_DIRS := $( SRC_DIRS) src/pc src/pc/gfx src/pc/audio src/pc/controller
ASM_DIRS :=
e n d i f
BIN_DIRS := bin bin/$( VERSION)
ULTRA_SRC_DIRS := lib/src lib/src/math
@ -177,6 +232,15 @@ else
e n d i f
e n d i f
i f e q ( $( TARGET_N 64) , 0 )
OPT_FLAGS += $( BITS)
e n d i f
i f e q ( $( TARGET_WEB ) , 1 )
OPT_FLAGS := -O2 -g4 --source-map-base http://localhost:8080/
e n d i f
e n d i f
# Use a default opt flag for gcc
i f e q ( $( COMPILER ) , g c c )
OPT_FLAGS := -O2
@ -188,11 +252,80 @@ include Makefile.split
# Source code files
LEVEL_C_FILES := $( wildcard levels/*/leveldata.c) $( wildcard levels/*/script.c) $( wildcard levels/*/geo.c)
C_FILES := $( foreach dir,$( SRC_DIRS) ,$( wildcard $( dir) /*.c) ) $( LEVEL_C_FILES)
CXX_FILES := $( foreach dir,$( SRC_DIRS) ,$( wildcard $( dir) /*.cpp) )
S_FILES := $( foreach dir,$( ASM_DIRS) ,$( wildcard $( dir) /*.s) )
ULTRA_C_FILES := $( foreach dir,$( ULTRA_SRC_DIRS) ,$( wildcard $( dir) /*.c) )
GODDARD_C_FILES := $( foreach dir,$( GODDARD_SRC_DIRS) ,$( wildcard $( dir) /*.c) )
ULTRA_S_FILES := $( foreach dir,$( ULTRA_ASM_DIRS) ,$( wildcard $( dir) /*.s) )
GENERATED_C_FILES := $( BUILD_DIR) /assets/mario_anim_data.c $( BUILD_DIR) /assets/demo_data.c
i f e q ( $( TARGET_N 64) , 1 )
ULTRA_S_FILES := $( foreach dir,$( ULTRA_ASM_DIRS) ,$( wildcard $( dir) /*.s) )
e n d i f
GENERATED_C_FILES := $( BUILD_DIR) /assets/mario_anim_data.c $( BUILD_DIR) /assets/demo_data.c \
$( addprefix $( BUILD_DIR) /bin/,$( addsuffix _skybox.c,$( notdir $( basename $( wildcard textures/skyboxes/*.png) ) ) ) )
i f e q ( $( WINDOWS_BUILD ) , 0 )
CXX_FILES :=
e n d i f
i f n e q ( $( TARGET_N 64) , 1 )
ULTRA_C_FILES_SKIP := \
sqrtf.c \
string.c \
sprintf.c \
_Printf.c \
kdebugserver.c \
osInitialize.c \
func_802F7140.c \
func_802F71F0.c \
func_802F4A20.c \
EU_D_802f4330.c \
D_802F4380.c \
osLeoDiskInit.c \
osCreateThread.c \
osDestroyThread.c \
osStartThread.c \
osSetThreadPri.c \
osPiStartDma.c \
osPiRawStartDma.c \
osPiRawReadIo.c \
osPiGetCmdQueue.c \
osJamMesg.c \
osSendMesg.c \
osRecvMesg.c \
osSetEventMesg.c \
osTimer.c \
osSetTimer.c \
osSetTime.c \
osCreateViManager.c \
osViSetSpecialFeatures.c \
osVirtualToPhysical.c \
osViBlack.c \
osViSetEvent.c \
osViSetMode.c \
osViSwapBuffer.c \
osSpTaskLoadGo.c \
osCreatePiManager.c \
osGetTime.c \
osEepromProbe.c \
osEepromWrite.c \
osEepromLongWrite.c \
osEepromRead.c \
osEepromLongRead.c \
osContInit.c \
osContStartReadData.c \
osAiGetLength.c \
osAiSetFrequency.c \
osAiSetNextBuffer.c \
__osViInit.c \
__osSyncPutChars.c \
__osAtomicDec.c \
__osSiRawStartDma.c \
__osViSwapContext.c \
__osViGetCurrentContext.c \
__osDevMgrMain.c
C_FILES := $( filter-out src/game/main.c,$( C_FILES) )
ULTRA_C_FILES := $( filter-out $( addprefix lib/src/,$( ULTRA_C_FILES_SKIP) ) ,$( ULTRA_C_FILES) )
e n d i f
i f e q ( $( VERSION ) , s h )
SOUND_BANK_FILES := $( wildcard sound/sound_banks/*.json)
@ -220,6 +353,7 @@ SOUND_OBJ_FILES := $(SOUND_BIN_DIR)/sound_data.ctl.o \
# Object files
O_FILES := $( foreach file,$( C_FILES) ,$( BUILD_DIR) /$( file:.c= .o) ) \
$( foreach file,$( CXX_FILES) ,$( BUILD_DIR) /$( file:.cpp= .o) ) \
$( foreach file,$( S_FILES) ,$( BUILD_DIR) /$( file:.s= .o) ) \
$( foreach file,$( GENERATED_C_FILES) ,$( file:.c= .o) )
@ -232,21 +366,27 @@ GODDARD_O_FILES := $(foreach file,$(GODDARD_C_FILES),$(BUILD_DIR)/$(file:.c=.o))
DEP_FILES := $( O_FILES:.o= .d) $( ULTRA_O_FILES:.o= .d) $( GODDARD_O_FILES:.o= .d) $( BUILD_DIR) /$( LD_SCRIPT) .d
# Files with GLOBAL_ASM blocks
i f n e q ( $( NON_MATCHING ) , 1 )
GLOBAL_ASM_C_FILES != grep -rl 'GLOBAL_ASM(' $( wildcard src/**/*.c)
GLOBAL_ASM_O_FILES = $( foreach file,$( GLOBAL_ASM_C_FILES) ,$( BUILD_DIR) /$( file:.c= .o) )
GLOBAL_ASM_DEP = $( BUILD_DIR) /src/audio/non_matching_dep
e n d i f
# Segment elf files
SEG_FILES := $( SEGMENT_ELF_FILES) $( ACTOR_ELF_FILES) $( LEVEL_ELF_FILES)
##################### Compiler Options #######################
INCLUDE_CFLAGS := -I include -I $( BUILD_DIR) -I $( BUILD_DIR) /include -I src -I .
ENDIAN_BITWIDTH := $( BUILD_DIR) /endian-and-bitwidth
i f e q ( $( TARGET_N 64) , 1 )
IRIX_ROOT := tools/ido5.3_compiler
i f e q ( $( shell type mips -linux -gnu -ld >/dev /null 2>/dev /null ; echo $ $ ?) , 0 )
CROSS := mips-linux-gnu-
e l s e i f e q ( $( shell type mips 64-linux -gnu -ld >/dev /null 2>/dev /null ; echo $ $ ?) , 0 )
CROSS := mips64-linux-gnu-
e l s e
e l s e i f e q ( $( shell type mips 64-elf -ld >/dev /null 2>/dev /null ; echo $ $ ?) , 0 )
CROSS := mips64-elf-
e n d i f
@ -277,8 +417,6 @@ ifeq ($(TARGET_N64),1)
CC_CFLAGS := -fno-builtin
e n d i f
INCLUDE_CFLAGS := -I include -I $( BUILD_DIR) -I $( BUILD_DIR) /include -I src -I .
# Check code syntax with host compiler
CC_CHECK := gcc -fsyntax-only -fsigned-char $( CC_CFLAGS) $( TARGET_CFLAGS) $( INCLUDE_CFLAGS) -std= gnu90 -Wall -Wextra -Wno-format-security -Wno-main -DNON_MATCHING -DAVOID_UB $( VERSION_CFLAGS) $( GRUCODE_CFLAGS)
@ -300,7 +438,51 @@ ifeq ($(shell getconf LONG_BIT), 32)
export QEMU_GUEST_BASE := 1
e l s e
# Ensure that gcc treats the code as 32-bit
CC_CHECK += -m32
CC_CHECK += $( BITS)
e n d i f
e l s e # TARGET_N64
AS := as
i f n e q ( $( TARGET_WEB ) , 1 )
CC := $( CROSS) gcc
CXX := $( CROSS) g++
e l s e
CC := emcc
e n d i f
i f e q ( $( WINDOWS_BUILD ) , 1 )
LD := $( CXX)
e l s e
LD := $( CC)
e n d i f
CPP := cpp -P
OBJDUMP := objdump
OBJCOPY := objcopy
PYTHON := python3
i f e q ( $( WINDOWS_BUILD ) , 1 )
CC_CHECK := $( CC) -fsyntax-only -fsigned-char $( INCLUDE_CFLAGS) -Wall -Wextra -Wno-format-security $( VERSION_CFLAGS) $( GRUCODE_CFLAGS) ` $( CROSS) sdl2-config --cflags`
CFLAGS := $( OPT_FLAGS) $( INCLUDE_CFLAGS) $( VERSION_CFLAGS) $( GRUCODE_CFLAGS) -fno-strict-aliasing -fwrapv ` $( CROSS) sdl2-config --cflags`
e l s e i f e q ( $( TARGET_WEB ) , 1 )
CC_CHECK := $( CC) -fsyntax-only -fsigned-char $( INCLUDE_CFLAGS) -Wall -Wextra -Wno-format-security $( VERSION_CFLAGS) $( GRUCODE_CFLAGS) -s USE_SDL = 2
CFLAGS := $( OPT_FLAGS) $( INCLUDE_CFLAGS) $( VERSION_CFLAGS) $( GRUCODE_CFLAGS) -fno-strict-aliasing -fwrapv -s USE_SDL = 2
e l s e
CC_CHECK := $( CC) -fsyntax-only -fsigned-char $( INCLUDE_CFLAGS) -Wall -Wextra -Wno-format-security $( VERSION_CFLAGS) $( GRUCODE_CFLAGS) ` $( CROSS) pkg-config --cflags libusb-1.0 glfw3` ` $( CROSS) sdl2-config --cflags`
CFLAGS := $( OPT_FLAGS) $( INCLUDE_CFLAGS) $( VERSION_CFLAGS) $( GRUCODE_CFLAGS) -fno-strict-aliasing -fwrapv ` $( CROSS) pkg-config --cflags libusb-1.0 glfw3` ` $( CROSS) sdl2-config --cflags`
e n d i f
ASFLAGS := -I include -I $( BUILD_DIR) $( VERSION_ASFLAGS)
i f e q ( $( TARGET_WEB ) , 1 )
LDFLAGS := -lm -lGL -lSDL2 -no-pie -s TOTAL_MEMORY = 20MB -g4 --source-map-base http://localhost:8080/ -s "EXTRA_EXPORTED_RUNTIME_METHODS=['callMain']"
e l s e
i f e q ( $( WINDOWS_BUILD ) , 1 )
LDFLAGS := $( BITS) -march= $( TARGET_ARCH) -Llib -lpthread -lglew32 ` $( CROSS) sdl2-config --static-libs` -lm -lglu32 -lsetupapi -ldinput8 -luser32 -lgdi32 -limm32 -lole32 -loleaut32 -lshell32 -lwinmm -lversion -luuid -lopengl32 -no-pie -static
e l s e
LDFLAGS := $( BITS) -march= $( TARGET_ARCH) -lm -lGL ` $( CROSS) sdl2-config --libs` -no-pie -lpthread ` $( CROSS) pkg-config --libs libusb-1.0 glfw3` -lasound -lX11 -lXrandr -lpulse
e n d i f
e n d i f
e n d i f
# Prevent a crash with -sopt
@ -333,23 +515,32 @@ endif
###################### Dependency Check #####################
i f e q ( $( TARGET_N 64) , 1 )
BINUTILS_VER_MAJOR := $( shell $( LD) --version | grep ^GNU | sed 's/^.* //; s/\..*//g' )
BINUTILS_VER_MINOR := $( shell $( LD) --version | grep ^GNU | sed 's/^[^.]*\.//; s/\..*//g' )
BINUTILS_DEPEND := $( shell expr $( BINUTILS_VER_MAJOR) \> = 2 \& $( BINUTILS_VER_MINOR) \> = 27)
i f e q ( $( BINUTILS_DEPEND ) , 0 )
$( error binutils version 2.27 required , version $ ( BINUTILS_VER_MAJOR ) .$ ( BINUTILS_VER_MINOR ) detected )
e n d i f
e n d i f
######################## Targets #############################
i f e q ( $( TARGET_N 64) , 1 )
all : $( ROM )
i f e q ( $( COMPARE ) , 1 )
@$( SHA1SUM) -c $( TARGET) .sha1 || ( echo 'The build succeeded, but did not match the official ROM. This is expected if you are making changes to the game.\nTo silence this message, use "make COMPARE=0"' . && false )
e n d i f
e l s e
all : $( EXE )
e n d i f
clean :
$( RM) -r $( BUILD_DIR_BASE)
cleantools :
$( MAKE) -s -C tools clean
distclean :
$( RM) -r $( BUILD_DIR_BASE)
./extract_assets.py --clean
@ -402,6 +593,7 @@ $(BUILD_DIR)/bin/segment2.o: $(BUILD_DIR)/text/$(VERSION)/define_text.inc.c
e n d i f
e n d i f
$(BUILD_DIR)/text/%/define_courses.inc.c : text /define_courses .inc .c text /%/courses .h
$( CPP) $( VERSION_CFLAGS) $< -o $@ -I text/$* /
$( TEXTCONV) charmap.txt $@ $@
@ -410,6 +602,8 @@ $(BUILD_DIR)/text/%/define_text.inc.c: text/define_text.inc.c text/%/courses.h t
$( CPP) $( VERSION_CFLAGS) $< -o $@ -I text/$* /
$( TEXTCONV) charmap.txt $@ $@
O_FILES += $( wildcard $( BUILD_DIR) /bin/$( VERSION) /*.o)
ALL_DIRS := $( BUILD_DIR) $( addprefix $( BUILD_DIR) /,$( SRC_DIRS) $( ASM_DIRS) $( GODDARD_SRC_DIRS) $( ULTRA_SRC_DIRS) $( ULTRA_ASM_DIRS) $( ULTRA_BIN_DIRS) $( BIN_DIRS) $( TEXTURE_DIRS) $( TEXT_DIRS) $( SOUND_SAMPLE_DIRS) $( addprefix levels/,$( LEVEL_DIRS) ) include) $( MIO0_DIR) $( addprefix $( MIO0_DIR) /,$( VERSION) ) $( SOUND_BIN_DIR) $( SOUND_BIN_DIR) /sequences/$( VERSION)
# Make sure build directory exists before compiling anything
@ -444,6 +638,7 @@ $(BUILD_DIR)/%.ci4: %.ci4.png
# compressed segment generation
i f e q ( $( TARGET_N 64) , 1 )
# TODO: ideally this would be `-Trodata-segment=0x07000000` but that doesn't set the address
$(BUILD_DIR)/bin/%.elf : $( BUILD_DIR ) /bin /%.o
@ -473,6 +668,7 @@ $(BUILD_DIR)/%.mio0.o: $(BUILD_DIR)/%.mio0.s
$(BUILD_DIR)/%.mio0.s : $( BUILD_DIR ) /%.mio 0
printf " .section .data\n\n.incbin \" $<\"\n " > $@
e n d i f
$(BUILD_DIR)/%.table : %.aiff
$( AIFF_EXTRACT_CODEBOOK) $< >$@
@ -510,8 +706,30 @@ $(SOUND_BIN_DIR)/%.m64: $(SOUND_BIN_DIR)/%.o
$(SOUND_BIN_DIR)/%.o : $( SOUND_BIN_DIR ) /%.s
$( AS) $( ASFLAGS) -o $@ $<
i f e q ( $( TARGET_N 64) , 1 )
$(SOUND_BIN_DIR)/%.s : $( SOUND_BIN_DIR ) /%
printf " .section .data\n\n.incbin \" $<\"\n " > $@
e l s e
$(SOUND_BIN_DIR)/sound_data.ctl.c : $( SOUND_BIN_DIR ) /sound_data .ctl
echo "unsigned char gSoundDataADSR[] = {" > $@
hexdump -v -e '1/1 "0x%X,"' $< >> $@
echo "};" >> $@
$(SOUND_BIN_DIR)/sound_data.tbl.c : $( SOUND_BIN_DIR ) /sound_data .tbl
echo "unsigned char gSoundDataRaw[] = {" > $@
hexdump -v -e '1/1 "0x%X,"' $< >> $@
echo "};" >> $@
$(SOUND_BIN_DIR)/sequences.bin.c : $( SOUND_BIN_DIR ) /sequences .bin
echo "unsigned char gMusicData[] = {" > $@
hexdump -v -e '1/1 "0x%X,"' $< >> $@
echo "};" >> $@
$(SOUND_BIN_DIR)/bank_sets.c : $( SOUND_BIN_DIR ) /bank_sets
echo "unsigned char gBankSetsData[] = {" > $@
hexdump -v -e '1/1 "0x%X,"' $< >> $@
echo "};" >> $@
e n d i f
$(BUILD_DIR)/levels/scripts.o : $( BUILD_DIR ) /include /level_headers .h
@ -578,6 +796,10 @@ $(GLOBAL_ASM_DEP).$(NON_MATCHING):
@rm -f $( GLOBAL_ASM_DEP) .*
touch $@
$(BUILD_DIR)/%.o : %.cpp
@$( CXX) -fsyntax-only $( CFLAGS) -MMD -MP -MT $@ -MF $( BUILD_DIR) /$* .d $<
$( CXX) -c $( CFLAGS) -o $@ $<
$(BUILD_DIR)/%.o : %.c
@$( CC_CHECK) -MMD -MP -MT $@ -MF $( BUILD_DIR) /$* .d $<
$( CC) -c $( CFLAGS) -o $@ $<
@ -590,6 +812,7 @@ $(BUILD_DIR)/%.o: $(BUILD_DIR)/%.c
$(BUILD_DIR)/%.o : %.s
$( AS) $( ASFLAGS) -MD $( BUILD_DIR) /$* .d -o $@ $<
i f e q ( $( TARGET_N 64) , 1 )
$(BUILD_DIR)/$(LD_SCRIPT) : $( LD_SCRIPT )
$( CPP) $( VERSION_CFLAGS) -MMD -MP -MT $@ -MF $@ .d -I include/ -I . -DBUILD_DIR= $( BUILD_DIR) -o $@ $<
@ -610,6 +833,11 @@ $(ROM): $(ELF)
$(BUILD_DIR)/$(TARGET).objdump : $( ELF )
$( OBJDUMP) -D $< > $@
e l s e
$(EXE) : $( O_FILES ) $( MIO 0_FILES :.mio 0=.o ) $( SOUND_OBJ_FILES ) $( ULTRA_O_FILES ) $( GODDARD_O_FILES )
$( LD) -L $( BUILD_DIR) -o $@ $( O_FILES) $( SOUND_OBJ_FILES) $( ULTRA_O_FILES) $( GODDARD_O_FILES) $( LDFLAGS)
e n d i f
.PHONY : all clean distclean default diff test load libultra