diff --git a/GBA.mk b/GBA.mk new file mode 100644 index 00000000..62221d21 --- /dev/null +++ b/GBA.mk @@ -0,0 +1,163 @@ +.PHONY: default +default: + @echo do not use this directly + +include Toolchain.mk + +GAME_VERSION ?= USA +BUILD_DIR = build/$(GAME_VERSION) + +TITLE := GBAZELDA MC +MAKER_CODE := 01 +REVISION := 0 + +ifeq ($(GAME_VERSION), EU) +GAME_CODE := BZMP +BUILD_NAME := tmc_eu +GAME_LANGUAGE := ENGLISH +TRANSLATIONS := translations/English.bin translations/French.bin translations/German.bin translations/Spanish.bin translations/Italian.bin +else ifeq ($(GAME_VERSION), JP) +GAME_CODE := BZMJ +BUILD_NAME := tmc_jp +GAME_LANGUAGE := JAPANESE +TRANSLATIONS := +else ifeq ($(GAME_VERSION), USA) +GAME_CODE := BZME +BUILD_NAME := tmc +GAME_LANGUAGE := ENGLISH +TRANSLATIONS := translations/USA.bin +else ifeq ($(GAME_VERSION), DEMO_USA) +GAME_CODE := BZHE +BUILD_NAME := tmc_demo_usa +GAME_LANGUAGE := ENGLISH +TRANSLATIONS := translations/USA.bin +else ifeq ($(GAME_VERSION), DEMO_JP) +GAME_CODE := BZMJ +BUILD_NAME := tmc_demo_jp +GAME_LANGUAGE := JAPANESE +TRANSLATIONS := +else +$(error unknown version $(GAME_VERSION)) +endif + +ROM = $(BUILD_NAME).gba +ELF = $(BUILD_NAME).elf + +# Clear the default suffixes +.SUFFIXES: +# Don't delete intermediate files +.SECONDARY: +# Delete files that weren't built properly +.DELETE_ON_ERROR: +# Secondary expansion is required for dependency variables in object rules. +.SECONDEXPANSION: + +# ================== +# entrypoint targets +# ================== +CUSTOM ?= +COMPARE ?= $(if $(CUSTOM),0,1) + +.PHONY: build extract_assets build_assets +build: $(if $(CUSTOM), build_assets, $(BUILD_DIR)/extracted_assets_$(GAME_VERSION)) + @$(MAKE) -f GBA.mk $(ROM) +ifeq ($(COMPARE), 1) + @$(SHA1) $(BUILD_NAME).sha1 +endif +extract_assets: $(BUILD_DIR)/converted_assets_$(GAME_VERSION) +# TODO this is slow, especially on builds with minor/no changes +build_assets: $(BUILD_DIR)/converted_assets_$(GAME_VERSION) + $(ASSET_PROCESSOR) build $(GAME_VERSION) $(BUILD_DIR)/assets + +.PHONY: clean +clean: + rm -rf build + rm -f t*.gba + rm -f t*.elf + +# =============== +# build ASM files +# =============== + +ASINCLUDE := -I $(BUILD_DIR)/assets -I $(BUILD_DIR)/enum_include +ASFLAGS := -mcpu=arm7tdmi --defsym $(GAME_VERSION)=1 --defsym REVISION=$(REVISION) --defsym $(GAME_LANGUAGE)=1 $(ASINCLUDE) + +# TODO try solve this without the glob +ENUM_ASM_SRCS := $(wildcard include/*.h) +ENUM_ASM_HEADERS := $(patsubst include/%.h,$(BUILD_DIR)/enum_include/%.inc,$(ENUM_ASM_SRCS)) + +# if this is too broad dependency scanning will clash with C file +$(BUILD_DIR)/asm/%.o: deps = $(shell $(SCANINC) -I . $(ASINCLUDE) $*.s) +$(BUILD_DIR)/data/%.o: deps = $(shell $(SCANINC) -I . $(ASINCLUDE) $*.s) +$(BUILD_DIR)/%.o: %.s $$(deps) $(ENUM_ASM_HEADERS) + @mkdir -p $(dir $@) + $(PREPROC) $(BUILD_NAME) $< -- $(ASINCLUDE) | $(AS) $(ASFLAGS) -o $@ + +$(BUILD_DIR)/enum_include/%.inc: include/%.h + @mkdir -p $(dir $@) + $(ENUM_PROCESSOR) $< $(CC) "-D__attribute__(x)=" "-D$(GAME_VERSION)" "-E" "-nostdinc" "-Itools/agbcc" "-Itools/agbcc/include" "-iquote include" > $@ + +# ============= +# build C files +# ============= + +# agbcc includes are separate because we don't want dependency scanning on them +CINCLUDE := -I include -I $(BUILD_DIR) +CPPFLAGS := -I tools/agbcc -I tools/agbcc/include $(CINCLUDE) -nostdinc -undef -D$(GAME_VERSION) -DREVISION=$(REVISION) -D$(GAME_LANGUAGE) +CFLAGS := -O2 -Wimplicit -Wparentheses -Werror -Wno-multichar -g3 + +interwork := $(BUILD_DIR)/src/interrupts.o \ +$(BUILD_DIR)/src/collision.o \ +$(BUILD_DIR)/src/playerItem.o \ +$(BUILD_DIR)/src/object.o \ +$(BUILD_DIR)/src/manager.o \ +$(BUILD_DIR)/src/npc.o \ +$(BUILD_DIR)/src/gba/m4a.o + +$(interwork): CFLAGS += -mthumb-interwork +$(BUILD_DIR)/src/eeprom.o: CFLAGS += -O1 -mthumb-interwork + +# if this is too broad dependency scanning will clash with ASM file +$(BUILD_DIR)/src/%.o: deps = $(shell $(SCANINC) $(CINCLUDE) $*.c) +$(BUILD_DIR)/%.o : %.c $$(deps) + @mkdir -p $(dir $@) + $(CPP) $(CPPFLAGS) $< -o $(BUILD_DIR)/$*.i + $(CC1) $(CFLAGS) -o $(BUILD_DIR)/$*.s $(BUILD_DIR)/$*.i + @echo "\t.text\n\t.align\t2, 0 @ Don't pad with nop\n" >> $(BUILD_DIR)/$*.s + $(AS) $(ASFLAGS) -o $@ $(BUILD_DIR)/$*.s + +# ============== +# build binaries +# ============== + +LDFLAGS = -Map ../../$(BUILD_DIR)/$(BUILD_NAME).map +LIB := -L ../../tools/agbcc/lib -lc + +$(ROM): $(ELF) + $(OBJCOPY) -O binary --gap-fill 0xFF --pad-to 0x9000000 $< $@ + +$(ELF): objs = $(shell grep -o -E "(\w|/)+\.o" linker.ld) +$(ELF): $(BUILD_DIR)/linker.ld $$(addprefix $(BUILD_DIR)/, $$(objs)) + cd $(BUILD_DIR) && $(LD) $(LDFLAGS) -n -T linker.ld -o ../../$@ $(LIB) + $(FIX) $@ -t"$(TITLE)" -c$(GAME_CODE) -m$(MAKER_CODE) -r$(REVISION) --silent + +$(BUILD_DIR)/linker.ld: linker.ld + @mkdir -p $(BUILD_DIR) + $(CPP) $(CPPFLAGS) -x c linker.ld | grep -v '^#' >$(BUILD_DIR)/linker.ld + +# ====== +# assets +# ====== + +$(BUILD_DIR)/extracted_assets_%: assets/assets.json assets/gfx.json assets/map.json assets/samples.json assets/sounds.json $(TRANSLATIONS) + @mkdir -p $(dir $@) + $(ASSET_PROCESSOR) extract $(GAME_VERSION) $(BUILD_DIR)/assets + touch $@ + +$(BUILD_DIR)/converted_assets_%: $(BUILD_DIR)/extracted_assets_% + @mkdir -p $(dir $@) + $(ASSET_PROCESSOR) convert $(GAME_VERSION) $(BUILD_DIR)/assets + touch $@ + +translations/%.bin: translations/%.json + tools/bin/tmc_strings -p --source $< --dest $@ diff --git a/INSTALL.md b/INSTALL.md index a9cedf71..398d07f0 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,14 +1,22 @@ # Install -First, you must put a The Legend of Zelda: The Minish Cap (U) ROM (with SHA1: `b4bd50e4131b027c334547b4524e2dbbd4227130`) in the root directory of the repository and name it `baserom.gba`. +This repository does not include any of the games assets. +To build the game using the decomp you need an original baserom for each version you want to build. +Put them with the appropriate filename into the repository root directory. +The supported versions are: -## Building other variants -To build other variants, you need to place the corresponding baserom before building, e.g. `baserom_jp.gba` for JP. +| Version | Filename | SHA1 | +|-----------------------|------------------------|--------------------------------------------| +| USA (project default) | `baserom.gba` | `b4bd50e4131b027c334547b4524e2dbbd4227130` | +| EU | `baserom_eu.gba` | `cff199b36ff173fb6faf152653d1bccf87c26fb7` | +| JP | `baserom_jp.gba` | `6c5404a1effb17f481f352181d0f1c61a2765c5d` | +| USA (Demo) | `baserom_demo_usa.gba` | `63fcad218f9047b6a9edbb68c98bd0dec322d7a1` | +| JP (Demo) | `baserom_demo_jp.gba` | `9cdb56fa79bba13158b81925c1f3641251326412` | ## Prerequisites -| Linux | macOS | Windows 10 (build 18917+) | Windows 10 (1709+) | Windows 8, 8.1, and 10 (1507, 1511, 1607, 1703) -| ----- | ----- | ------------------------- | ------------------ | --------------------------------------------------------- -| none | [Xcode Command Line Tools package][xcode] | [Windows Subsystem for Linux 2][wsl2] | [Windows Subsystem for Linux][wsl] | [Cygwin][cygwin] +| Linux | macOS | Windows 10 (build 18917+) | Windows 10 (1709+) | Windows 8, 8.1, and 10 (1507, 1511, 1607, 1703) | +|-------|-------------------------------------------|---------------------------------------|------------------------------------|-------------------------------------------------| +| none | [Xcode Command Line Tools package][xcode] | [Windows Subsystem for Linux 2][wsl2] | [Windows Subsystem for Linux][wsl] | [Cygwin][cygwin] | [xcode]: https://developer.apple.com/library/archive/technotes/tn2339/_index.html#//apple_ref/doc/uid/DTS40014588-CH1-DOWNLOADING_COMMAND_LINE_TOOLS_IS_NOT_AVAILABLE_IN_XCODE_FOR_MACOS_10_9__HOW_CAN_I_INSTALL_THEM_ON_MY_MACHINE_ [wsl2]: https://docs.microsoft.com/windows/wsl/wsl2-install @@ -17,53 +25,59 @@ To build other variants, you need to place the corresponding baserom before buil The [prerelease version of the Linux subsystem](https://docs.microsoft.com/windows/wsl/install-legacy) available in the 1607 and 1703 releases of Windows 10 is obsolete so consider uninstalling it. -Make sure that the `build-essential`, `git`, `python3`, `python3-pip`, `cmake` and `libpng-dev` packages are installed. The `build-essential` package includes the `make`, `gcc-core`, and `g++` packages so they do not have to be obtained separately. +Make sure that the `build-essential`, `git`, `python3`, `python3-pip`, `cmake` and `libpng-dev` packages are installed. The `build-essential` package includes the `make`, `gcc-core`, and `g++` packages, so they do not have to be obtained separately. In the case of Cygwin, [include](https://cygwin.com/cygwin-ug-net/setup-net.html#setup-packages) the `make`, `git`, `gcc-core`, `gcc-g++`, and `libpng-devel` packages. -Install the **devkitARM** toolchain of [devkitPro](https://devkitpro.org/wiki/Getting_Started) and add its environment variables. For Windows versions without the Linux subsystem, the devkitPro [graphical installer](https://github.com/devkitPro/installer/releases) includes a preconfigured MSYS2 environment, thus the steps below are not required. +To build the games code, the `arm-none-eabi-gcc` compiler is required. +Both a standalone installation and [devkitPro](https://devkitpro.org/wiki/Getting_Started) are supported. +For devkitPro, install the `gba-dev` package. - sudo (dkp-)pacman -S gba-dev - export DEVKITPRO=/opt/devkitpro - echo "export DEVKITPRO=$DEVKITPRO" >> ~/.bashrc - export DEVKITARM=$DEVKITPRO/devkitARM - echo "export DEVKITARM=$DEVKITARM" >> ~/.bashrc +If `arm-none-eabi-gcc` is not available through `PATH` use `TOOLCHAIN_PATH=` to indicate its location. +This is not required for devkitPro, the `DEVKITARM` environment variable is used for auto-detection. -Install the pycparser python package: - pip3 install pycparser +Install `python3` and the `pycparser` python package: +`pip3 install pycparser` ## Installation To set up the repository: +```shell +git clone https://github.com/zeldaret/tmc +git clone https://github.com/pret/agbcc - git clone https://github.com/zeldaret/tmc - git clone https://github.com/pret/agbcc +cd ./agbcc +sh build.sh +sh install.sh ../tmc - cd ./agbcc - sh build.sh - sh install.sh ../tmc +cd ../tmc +make tools +``` +To build `tmc.gba`: +```shell +make -j$(nproc) +``` +>**Note:** If the build command is not recognized on Linux, including the Linux environment used within Windows, run `nproc` and replace `$(nproc)` with the returned value (e.g.: `make -j4`). Because `nproc` is not available on macOS, the alternative is `sysctl -n hw.ncpu`. - cd ../tmc - make setup +You can configure the game version built by using the `GAME_VERSION` variable (ie. `make GAME_VERSION=EU`). +Convenience targets for all 5 versions exist (`make usa eu jp demo_usa demo_jp`). +`make all` builds all 5 versions. -To build **tmc.gba**: +If you modify the game you need to do a custom build. +Use `CUSTOM=1` for that (any nonempty value will enable it, so `CUSTOM=0` will NOT disable it). +There is a convenience target `make custom` that does a custom USA build. - make -j$(nproc) - -If only `.c` or `.s` files were changed, turn off the dependency scanning temporarily. Changes to any other files will be ignored and the build will either fail or not reflect those changes. - - make -j$(nproc) NODEP=1 - -**Note:** If the build command is not recognized on Linux, including the Linux environment used within Windows, run `nproc` and replace `$(nproc)` with the returned value (e.g.: `make -j4`). Because `nproc` is not available on macOS, the alternative is `sysctl -n hw.ncpu`. +The `COMPARE` variable controls the SHA1 verification check. +It is enabled (`1`) for normal builds and disabled (`0`) for custom builds by default. ### Note for Mac users The BSD make that comes with Mac XCode can be buggy, so obtain GNU make and sed using [Homebrew](https://brew.sh): - - brew install make gnu-sed - +```shell +brew install make gnu-sed +``` When compiling agbcc, substitute the `build.sh` line for - - gsed 's/^make/gmake/g' build.sh | sh - +```shell +gsed 's/^make/gmake/g' build.sh | sh +``` Finally, use `gmake` instead of `make` to compile the ROM(s). diff --git a/Jenkinsfile b/Jenkinsfile index 603ff83a..e4aba2b1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -12,12 +12,12 @@ pipeline { sh 'cp /usr/local/etc/roms/tmc.demo.jp.gba baserom_demo_jp.gba' sh 'cp /usr/local/etc/roms/tmc.jp.gba baserom_jp.gba' sh 'cp /usr/local/etc/roms/tmc.eu.gba baserom_eu.gba' - sh 'make -j setup' + sh 'make tools' } } stage('Build') { steps { - sh 'make usa demo_usa jp demo_jp eu -j' + sh 'make all -j' } } stage('Report Progress') { diff --git a/Makefile b/Makefile index cdf51352..394eacea 100644 --- a/Makefile +++ b/Makefile @@ -1,294 +1,41 @@ -include $(DEVKITARM)/base_tools - -COMPARE ?= 0 - -CPP := $(CC) -E -LD := $(DEVKITARM)/bin/arm-none-eabi-ld - -GAME_VERSION ?= USA -REVISION := 0 -GAME_LANGUAGE := ENGLISH - -TITLE := GBAZELDA MC -MAKER_CODE := 01 - -ifeq ($(GAME_VERSION), USA) -GAME_CODE := BZME -BUILD_NAME := tmc -else -ifeq ($(GAME_VERSION), DEMO_USA) -GAME_CODE := BZHE -BUILD_NAME := tmc_demo_usa -else -ifeq ($(GAME_VERSION), JP) -GAME_CODE := BZMJ -BUILD_NAME := tmc_jp -GAME_LANGUAGE := JAPANESE -else -ifeq ($(GAME_VERSION), DEMO_JP) -GAME_CODE := BZMJ -BUILD_NAME := tmc_demo_jp -GAME_LANGUAGE := JAPANESE -else -ifeq ($(GAME_VERSION), EU) -GAME_CODE := BZMP -BUILD_NAME := tmc_eu -else -$(error unknown version $(GAME_VERSION)) -endif -endif -endif -endif -endif - -SHELL := /bin/bash -o pipefail - - -ROM := $(BUILD_NAME).gba -OBJ_DIR := build/$(BUILD_NAME) - -ELF = $(ROM:.gba=.elf) -MAP = $(ROM:.gba=.map) - -C_SUBDIR = src -DATA_C_SUBDIR = src/data -ASM_SUBDIR = asm -DATA_ASM_SUBDIR = data -SONG_SUBDIR = sound/songs -MID_SUBDIR = sound/songs/midi -ASSET_SUBDIR = assets -ENUM_INCLUDE_SUBDIR = enum_include - -C_BUILDDIR = $(OBJ_DIR)/$(C_SUBDIR) -ASM_BUILDDIR = $(OBJ_DIR)/$(ASM_SUBDIR) -ASM_ENUM_INCLUDE_DIR = $(ASM_BUILDDIR)/$(ENUM_INCLUDE_SUBDIR) -DATA_ASM_BUILDDIR = $(OBJ_DIR)/$(DATA_ASM_SUBDIR) -SONG_BUILDDIR = $(OBJ_DIR)/$(SONG_SUBDIR) -MID_BUILDDIR = $(OBJ_DIR)/$(MID_SUBDIR) -ASSET_BUILDDIR = $(OBJ_DIR)/$(ASSET_SUBDIR) -PREPROC_INC_PATHS = $(ASSET_BUILDDIR) $(ASM_ENUM_INCLUDE_DIR) - -ASFLAGS := -mcpu=arm7tdmi --defsym $(GAME_VERSION)=1 --defsym REVISION=$(REVISION) --defsym $(GAME_LANGUAGE)=1 -I $(ASSET_SUBDIR) -I $(ASSET_BUILDDIR) -I $(ASM_ENUM_INCLUDE_DIR) - -CC1 := tools/agbcc/bin/agbcc -override CFLAGS += -O2 -Wimplicit -Wparentheses -Werror -Wno-multichar -g3 -# -fhex-asm - -# ifeq ($(DINFO),1) -# override CFLAGS += -g -# endif - -CPPFLAGS := -I tools/agbcc -I tools/agbcc/include -iquote include -nostdinc -undef -D$(GAME_VERSION) -DREVISION=$(REVISION) -D$(GAME_LANGUAGE) -I $(OBJ_DIR) - -LDFLAGS = -Map ../../$(MAP) - -LIB := -L ../../tools/agbcc/lib -lc - -SHA1 := $(shell { command -v sha1sum || command -v shasum; } 2>/dev/null) -c -GFX := tools/bin/gbagfx -AIF := tools/bin/aif2pcm -MID := tools/bin/mid2agb -SCANINC := tools/bin/scaninc -# TODO: use charmap? -PREPROC := tools/bin/preproc -FIX := tools/bin/gbafix -ASSET_PROCESSOR := tools/bin/asset_processor -ENUM_PROCESSOR := tools/extract_include_enum.py - -ASSET_CONFIGS = assets/assets.json assets/gfx.json assets/map.json assets/samples.json assets/sounds.json -TRANSLATIONS = translations/USA.bin translations/English.bin translations/French.bin translations/German.bin translations/Spanish.bin translations/Italian.bin - -# Clear the default suffixes -.SUFFIXES: -# Don't delete intermediate files -.SECONDARY: -# Delete files that weren't built properly -.DELETE_ON_ERROR: -# Secondary expansion is required for dependency variables in object rules. -.SECONDEXPANSION: - - -$(shell mkdir -p $(C_BUILDDIR) $(ASM_BUILDDIR) $(DATA_ASM_BUILDDIR) $(SONG_BUILDDIR) $(MID_BUILDDIR)) - -infoshell = $(foreach line, $(shell $1 | sed "s/ /__SPACE__/g"), $(info $(subst __SPACE__, ,$(line)))) - -# Build tools when building the rom -# Disable dependency scanning for clean/tidy/tools -ifeq (,$(filter-out all compare target,$(MAKECMDGOALS))) -$(call infoshell, $(MAKE) tools) -else -NODEP := 1 -endif - -interwork := $(C_BUILDDIR)/interrupts.o \ -$(C_BUILDDIR)/collision.o \ -$(C_BUILDDIR)/playerItem.o \ -$(C_BUILDDIR)/object.o \ -$(C_BUILDDIR)/manager.o \ -$(C_BUILDDIR)/npc.o - -$(interwork): CFLAGS += -mthumb-interwork -$(C_BUILDDIR)/gba/m4a.o: CFLAGS = -O2 -mthumb-interwork -Wimplicit -Wparentheses -Werror -Wno-multichar -$(C_BUILDDIR)/eeprom.o: CFLAGS = -O1 -mthumb-interwork -Wimplicit -Wparentheses -Werror -Wno-multichar - -C_SRCS := $(wildcard $(C_SUBDIR)/*.c $(C_SUBDIR)/*/*.c) -C_OBJS := $(patsubst $(C_SUBDIR)/%.c,$(C_BUILDDIR)/%.o,$(C_SRCS)) - -ASM_SRCS := $(wildcard $(ASM_SUBDIR)/*.s $(ASM_SUBDIR)/*/*.s) -ASM_OBJS := $(patsubst $(ASM_SUBDIR)/%.s,$(ASM_BUILDDIR)/%.o,$(ASM_SRCS)) $(patsubst $(ASM_SUBDIR)/*/%.s,$(ASM_BUILDDIR)/**/%.o,$(ASM_SRCS)) - -DATA_ASM_SRCS := $(wildcard $(DATA_ASM_SUBDIR)/*.s $(DATA_ASM_SUBDIR)/**/*.s $(DATA_ASM_SUBDIR)/**/**/*.s) -DATA_ASM_OBJS := $(patsubst $(DATA_ASM_SUBDIR)/%.s,$(DATA_ASM_BUILDDIR)/%.o,$(DATA_ASM_SRCS)) - -SONG_SRCS := $(wildcard $(SONG_SUBDIR)/*.s) -SONG_OBJS := $(patsubst $(SONG_SUBDIR)/%.s,$(SONG_BUILDDIR)/%.o,$(SONG_SRCS)) - -MID_SRCS := $(wildcard $(MID_SUBDIR)/*.mid) -MID_OBJS := $(patsubst $(MID_SUBDIR)/%.mid,$(MID_BUILDDIR)/%.o,$(MID_SRCS)) - -ENUM_ASM_SRCS := $(wildcard include/*.h) -ENUM_ASM_HEADERS := $(patsubst include/%.h,$(ASM_ENUM_INCLUDE_DIR)/%.inc,$(ENUM_ASM_SRCS)) - -OBJS := $(C_OBJS) $(ASM_OBJS) $(DATA_ASM_OBJS) $(SONG_OBJS) $(MID_OBJS) -OBJS_REL := $(patsubst $(OBJ_DIR)/%,%,$(OBJS)) - -SUBDIRS := $(sort $(dir $(OBJS) $(ENUM_ASM_HEADERS))) - -$(shell mkdir -p $(SUBDIRS)) - -.PHONY: all setup clean-tools mostlyclean clean tidy tools extractassets buildassets custom +.PHONY: default all +default: build +all: eu jp usa demo_jp demo_usa MAKEFLAGS += --no-print-directory -AUTO_GEN_TARGETS := +.PHONY: build eu jp usa demo_jp demo_usa custom +build: GAME_VERSION ?=USA +build: tools + @$(MAKE) -f GBA.mk build GAME_VERSION=$(GAME_VERSION) -# TODO do we really need this extra step just so that the assets are always extracted at first? -all: build/extracted_assets_$(GAME_VERSION) - @$(MAKE) target GAME_VERSION=$(GAME_VERSION) +eu: GAME_VERSION=EU +jp: GAME_VERSION=JP +usa: GAME_VERSION=USA +demo_jp: GAME_VERSION=DEMO_JP +demo_usa: GAME_VERSION=DEMO_USA +eu jp usa demo_jp demo_usa: tools + @$(MAKE) GAME_VERSION=$(GAME_VERSION) -target: $(ROM) - @$(SHA1) $(BUILD_NAME).sha1 +custom: tools + @$(MAKE) GAME_VERSION=USA CUSTOM=1 -custom: buildassets - @$(MAKE) target GAME_VERSION=$(GAME_VERSION) +.PHONY: extract_assets +extract_assets: tools + @$(MAKE) -f GBA.mk extract_assets -# kept for backwards compat -compare: $(ROM) - @$(SHA1) $(BUILD_NAME).sha1 +.PHONY: tools +tools: tools/bin -setup: tools - -# all tools are build at once -# FIXME figure out why make builds multiple times when specifying all tools here -tools: $(GFX) - -$(GFX) $(AIF) $(MID) $(SCANINC) $(PREPROC) $(FIX) $(ASSET_PROCESSOR) tools/bin/agb2mid tools/bin/tmc_strings tools/bin/bin2c &: +tools/bin: mkdir -p tools/cmake-build - unset CC CXX AS LD LDFLAGS && cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=tools -S tools -B tools/cmake-build + cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=tools -S tools -B tools/cmake-build cmake --build tools/cmake-build -j --target install -# Automatically extract binary data -build/extracted_assets_%: $(ASSET_CONFIGS) $(TRANSLATIONS) - $(ASSET_PROCESSOR) extract $(GAME_VERSION) $(ASSET_BUILDDIR) - touch $@ - -# Extract assets to human readable form -extractassets: - $(ASSET_PROCESSOR) convert $(GAME_VERSION) $(ASSET_BUILDDIR) - -# Build the assets from the human readable form -buildassets: - $(ASSET_PROCESSOR) build $(GAME_VERSION) $(ASSET_BUILDDIR) - -mostlyclean: tidy - rm -f sound/direct_sound_samples/*.bin - rm -f $(SONG_OBJS) $(MID_SUBDIR)/*.s - find . \( -iname '*.1bpp' -o -iname '*.4bpp' -o -iname '*.8bpp' -o -iname '*.gbapal' -o -iname '*.lz' -o -iname '*.latfont' -o -iname '*.hwjpnfont' -o -iname '*.fwjpnfont' \) -exec rm {} + - rm -f $(AUTO_GEN_TARGETS) +.PHONY: clean clean-tools +clean: + @$(MAKE) -f GBA.mk clean clean-tools: rm -rf tools/bin rm -rf tools/cmake-build - -clean: mostlyclean clean-tools - -tidy: - rm -f tmc.gba tmc.elf tmc.map - rm -f tmc_demo_usa.gba tmc_demo_usa.elf tmc_demo_usa.map - rm -f tmc_jp.gba tmc_jp.elf tmc_jp.map - rm -f tmc_demo_jp.gba tmc_demo_jp.elf tmc_demo_jp.map - rm -f tmc_eu.gba tmc_eu.elf tmc_eu.map - rm -r build/* - - -%.s: ; -%.png: ; -%.pal: ; -%.aif: ; - -%.1bpp: %.png ; $(GFX) $< $@ -%.4bpp: %.png ; $(GFX) $< $@ -%.8bpp: %.png ; $(GFX) $< $@ -%.gbapal: %.pal ; $(GFX) $< $@ -%.gbapal: %.png ; $(GFX) $< $@ -%.lz: % ; $(GFX) $< $@ -%.rl: % ; $(GFX) $< $@ - cd $(@D) && ../../$(MID) $(> $(C_BUILDDIR)/$*.s - $(AS) $(ASFLAGS) -o $@ $(C_BUILDDIR)/$*.s - -ifeq ($(NODEP),1) -$(ASM_BUILDDIR)/%.o: asm_dep := -else -$(ASM_BUILDDIR)/%.o: asm_dep = $(shell $(SCANINC) -I . $(ASM_SUBDIR)/$*.s) -endif - -$(ASM_BUILDDIR)/%.o: $(ASM_SUBDIR)/%.s $$(asm_dep) - $(PREPROC) $(BUILD_NAME) $< -- $(PREPROC_INC_PATHS) | $(AS) $(ASFLAGS) -o $@ - -$(ASM_ENUM_INCLUDE_DIR)/%.inc: include/%.h - $(ENUM_PROCESSOR) $< $(CC) "-D__attribute__(x)=" "-D$(GAME_VERSION)" "-E" "-nostdinc" "-Itools/agbcc" "-Itools/agbcc/include" "-iquote include" > $@ - -ifeq ($(NODEP),1) -$(DATA_ASM_BUILDDIR)/%.o: data_dep := -else -$(DATA_ASM_BUILDDIR)/%.o: data_dep = $(shell $(SCANINC) -I . -I $(ASSET_SUBDIR) -I $(ASSET_BUILDDIR) -I $(ASM_ENUM_INCLUDE_DIR) $(DATA_ASM_SUBDIR)/$*.s) -endif - -$(DATA_ASM_BUILDDIR)/%.o: $(DATA_ASM_SUBDIR)/%.s $$(data_dep) $(ENUM_ASM_HEADERS) - $(PREPROC) $(BUILD_NAME) $< charmap.txt -- $(PREPROC_INC_PATHS) | $(CPP) -I include -nostdinc -undef -Wno-unicode - | $(AS) $(ASFLAGS) -o $@ - -$(SONG_BUILDDIR)/%.o: $(SONG_SUBDIR)/%.s - $(AS) $(ASFLAGS) -I sound -o $@ $< - -$(OBJ_DIR)/linker.ld: linker.ld - $(CPP) $(CPPFLAGS) -x c linker.ld | grep -v '^#' >$(OBJ_DIR)/linker.ld - -$(ELF): $(OBJS) $(OBJ_DIR)/linker.ld - cd $(OBJ_DIR) && $(LD) $(LDFLAGS) -n -T linker.ld -o ../../$@ $(LIB) - $(FIX) $@ -t"$(TITLE)" -c$(GAME_CODE) -m$(MAKER_CODE) -r$(REVISION) --silent - -$(ROM): $(ELF) - $(OBJCOPY) -O binary --gap-fill 0xFF --pad-to 0x9000000 $< $@ - -usa: ; @$(MAKE) GAME_VERSION=USA -demo_usa: ; @$(MAKE) GAME_VERSION=DEMO_USA -jp: ; @$(MAKE) GAME_VERSION=JP -demo_jp: ; @$(MAKE) GAME_VERSION=DEMO_JP -eu: ; @$(MAKE) GAME_VERSION=EU diff --git a/Toolchain.mk b/Toolchain.mk new file mode 100644 index 00000000..76e2a871 --- /dev/null +++ b/Toolchain.mk @@ -0,0 +1,38 @@ +# ======== +# compiler +# ======== + +ifndef TOOLCHAIN_PATH +ifneq (,$(shell which arm-none-eabi-gcc)) +TOOLCHAIN_PATH := +else ifdef DEVKITARM +TOOLCHAIN_PATH := $(DEVKITARM)/bin/ +else +$(error arm-none-eabi-gcc not found, please install (devkitPro supported)) +endif +endif + +# ensure trailing slash +ifneq ($(TOOLCHAIN_PATH),) +override TOOLCHAIN_PATH:=$(TOOLCHAIN_PATH)/ +endif + +CC := $(TOOLCHAIN_PATH)arm-none-eabi-gcc +CPP := $(CC) -E +CXX := $(TOOLCHAIN_PATH)arm-none-eabi-g++ +AS := $(TOOLCHAIN_PATH)arm-none-eabi-as +LD := $(TOOLCHAIN_PATH)arm-none-eabi-ld +OBJCOPY := $(TOOLCHAIN_PATH)arm-none-eabi-objcopy + +# ============ +# custom tools +# ============ + +CC1 := tools/agbcc/bin/agbcc +SHA1 := $(shell { command -v sha1sum || command -v shasum; } 2>/dev/null) -c + +SCANINC := tools/bin/scaninc +PREPROC := tools/bin/preproc +ASSET_PROCESSOR := tools/bin/asset_processor +ENUM_PROCESSOR := tools/extract_include_enum.py +FIX := tools/bin/gbafix diff --git a/asm/non_matching/gyorgTail/sub_080AC5E4.inc b/asm/non_matching/gyorgTail/sub_080AC5E4.inc deleted file mode 100644 index 1bd2d4b8..00000000 --- a/asm/non_matching/gyorgTail/sub_080AC5E4.inc +++ /dev/null @@ -1,134 +0,0 @@ - .syntax unified - push {r4, r5, r6, r7, lr} - mov r7, sb - mov r6, r8 - push {r6, r7} - adds r5, r0, #0 - ldrb r4, [r5, #0xa] - cmp r4, #0 - bne _080AC668 - ldr r0, _080AC664 @ =gEntCount - ldrb r0, [r0] - cmp r0, #0x43 - bhi _080AC670 - adds r1, r5, #0 - adds r1, #0x79 - movs r0, #0x11 - strb r0, [r1] - movs r0, #0x22 - bl CreateProjectile - adds r6, r0, #0 - ldrb r0, [r5, #0xa] - strb r0, [r6, #0xa] - movs r0, #1 - strb r0, [r6, #0xb] - ldr r0, [r5, #0x50] - str r0, [r6, #0x50] - adds r1, r6, #0 - adds r1, #0x79 - movs r0, #0x12 - strb r0, [r1] - str r6, [r5, #0x54] - movs r0, #0x22 - bl CreateProjectile - adds r7, r0, #0 - ldrb r0, [r5, #0xa] - strb r0, [r7, #0xa] - movs r0, #2 - strb r0, [r7, #0xb] - ldr r0, [r5, #0x50] - str r0, [r7, #0x50] - adds r0, r7, #0 - adds r0, #0x79 - movs r1, #0x14 - mov r8, r1 - mov r1, r8 - strb r1, [r0] - str r7, [r6, #0x54] - movs r0, #0x22 - bl CreateProjectile - ldrb r1, [r5, #0xa] - strb r1, [r0, #0xa] - movs r1, #3 - strb r1, [r0, #0xb] - ldr r1, [r5, #0x50] - str r1, [r0, #0x50] - str r4, [r0, #0x54] - adds r1, r0, #0 - adds r1, #0x79 - strb r4, [r1] - str r0, [r7, #0x54] - b _080AC6C8 - .align 2, 0 -_080AC664: .4byte gEntCount -_080AC668: - ldr r0, _080AC674 @ =gEntCount - ldrb r0, [r0] - cmp r0, #0x44 - bls _080AC678 -_080AC670: - movs r0, #0 - b _080AC6E8 - .align 2, 0 -_080AC674: .4byte gEntCount -_080AC678: - adds r1, r5, #0 - adds r1, #0x79 - movs r0, #0 - mov sb, r0 - movs r0, #0xf - strb r0, [r1] - movs r0, #0x22 - bl CreateProjectile - adds r6, r0, #0 - ldrb r0, [r5, #0xa] - strb r0, [r6, #0xa] - movs r0, #1 - strb r0, [r6, #0xb] - ldr r0, [r5, #0x50] - str r0, [r6, #0x50] - adds r0, r6, #0 - adds r0, #0x79 - movs r1, #0x10 - mov r8, r1 - mov r1, r8 - strb r1, [r0] - str r6, [r5, #0x54] - movs r0, #0x22 - bl CreateProjectile - adds r7, r0, #0 - ldrb r0, [r5, #0xa] - strb r0, [r7, #0xa] - movs r0, #2 - strb r0, [r7, #0xb] - ldr r0, [r5, #0x50] - str r0, [r7, #0x50] - mov r0, sb - str r0, [r7, #0x54] - adds r1, r7, #0 - adds r1, #0x79 - movs r0, #0x20 - strb r0, [r1] - str r7, [r6, #0x54] -_080AC6C8: - movs r0, #0x22 - bl CreateProjectile - adds r6, r0, #0 - ldrb r0, [r5, #0xa] - strb r0, [r6, #0xa] - movs r0, #4 - strb r0, [r6, #0xb] - ldr r0, [r5, #0x50] - str r0, [r6, #0x50] - str r5, [r6, #0x54] - adds r0, r6, #0 - adds r0, #0x79 - mov r1, r8 - strb r1, [r0] - movs r0, #1 -_080AC6E8: - pop {r3, r4} - mov r8, r3 - mov sb, r4 - pop {r4, r5, r6, r7, pc} - .syntax divided diff --git a/asm/non_matching/zeldaFollower/sub_08068318.inc b/asm/non_matching/zeldaFollower/sub_08068318.inc deleted file mode 100644 index 620448a5..00000000 --- a/asm/non_matching/zeldaFollower/sub_08068318.inc +++ /dev/null @@ -1,287 +0,0 @@ - .syntax unified - push {r4, r5, r6, r7, lr} - mov r7, sb - mov r6, r8 - push {r6, r7} - mov r8, r0 - ldr r4, _08068414 @ =gPlayerEntity - ldrh r1, [r4, #0x2e] - ldr r3, _08068418 @ =0xFFFF0000 - adds r0, r3, #0 - ands r0, r6 - orrs r0, r1 - ldrh r1, [r4, #0x32] - lsls r1, r1, #0x10 - ldr r2, _0806841C @ =0x0000FFFF - ands r0, r2 - adds r6, r0, #0 - orrs r6, r1 - ldrh r0, [r4, #0x36] - ands r3, r7 - orrs r3, r0 - adds r7, r3, #0 - ldr r0, _08068420 @ =gPlayerState - adds r0, #0xa8 - ldrb r0, [r0] - lsls r0, r0, #0x10 - ldr r2, _08068424 @ =0xFF00FFFF - ands r2, r3 - orrs r2, r0 - adds r7, r2, #0 - ldrb r1, [r4, #0x14] - movs r0, #0x3f - ands r1, r0 - lsls r1, r1, #0x18 - ldr r0, _08068428 @ =0xC0FFFFFF - ands r0, r2 - orrs r0, r1 - adds r7, r0, #0 - adds r1, r4, #0 - adds r1, #0x38 - ldrb r1, [r1] - lsls r1, r1, #0x1e - ldr r2, _0806842C @ =0x3FFFFFFF - ands r0, r2 - adds r7, r0, #0 - orrs r7, r1 - mov r0, r8 - ldr r5, [r0, #0x64] - ldrb r1, [r5, #6] - cmp r1, #0xa - bne _08068386 - lsrs r0, r7, #0x10 - lsls r0, r0, #0x18 - lsrs r0, r0, #0x18 - cmp r0, #0xa - bne _08068394 -_08068386: - cmp r1, #0x16 - bne _080683B4 - lsrs r0, r7, #0x10 - lsls r0, r0, #0x18 - lsrs r0, r0, #0x18 - cmp r0, #0x16 - beq _080683B4 -_08068394: - ldrh r0, [r4, #0x2e] - mov r1, r8 - strh r0, [r1, #0x2e] - ldrh r0, [r4, #0x32] - strh r0, [r1, #0x32] - ldrb r1, [r1, #0x18] - movs r0, #4 - rsbs r0, r0, #0 - ands r0, r1 - movs r1, #1 - orrs r0, r1 - mov r2, r8 - strb r0, [r2, #0x18] - mov r0, r8 - bl sub_08068578 -_080683B4: - movs r3, #0 - mov sb, r3 - ldr r0, [r5] - cmp r6, r0 - bne _080683D0 - ldr r0, [r5, #4] - cmp r7, r0 - bne _080683D0 - lsls r1, r7, #8 - lsrs r0, r1, #0x18 - cmp r0, #0x16 - beq _080683D0 - cmp r0, #0xa - bne _08068430 -_080683D0: - mov r0, r8 - ldr r5, [r0, #0x64] - adds r5, #0x90 - mov r4, r8 - adds r4, #0x69 - mov r3, r8 - adds r3, #0x38 - movs r1, #0x58 - add r1, r8 - mov ip, r1 - movs r2, #0x12 -_080683E6: - ldr r0, [r5] - ldr r1, [r5, #4] - str r0, [r5, #8] - str r1, [r5, #0xc] - subs r5, #8 - subs r2, #1 - cmp r2, #0 - bge _080683E6 - mov r2, r8 - ldr r5, [r2, #0x64] - str r6, [r5] - str r7, [r5, #4] - movs r0, #4 - mov sb, r0 - ldrb r1, [r4] - movs r0, #0 - ldrsb r0, [r4, r0] - cmp r0, #0 - ble _080684A8 - subs r0, r1, #1 - strb r0, [r4] - b _080684A8 - .align 2, 0 -_08068414: .4byte gPlayerEntity -_08068418: .4byte 0xFFFF0000 -_0806841C: .4byte 0x0000FFFF -_08068420: .4byte gPlayerState -_08068424: .4byte 0xFF00FFFF -_08068428: .4byte 0xC0FFFFFF -_0806842C: .4byte 0x3FFFFFFF -_08068430: - adds r5, #0x98 - movs r1, #4 - ldrsh r0, [r5, r1] - cmp r0, #0 - bge _08068466 - mov r2, r8 - ldr r5, [r2, #0x64] - adds r5, #0x90 - mov r4, r8 - adds r4, #0x69 - mov r3, r8 - adds r3, #0x38 - movs r0, #0x58 - add r0, r8 - mov ip, r0 - movs r2, #0x12 -_08068450: - ldr r0, [r5] - ldr r1, [r5, #4] - str r0, [r5, #8] - str r1, [r5, #0xc] - subs r5, #8 - subs r2, #1 - cmp r2, #0 - bge _08068450 - movs r1, #4 - mov sb, r1 - b _080684A8 -_08068466: - ldr r1, _0806851C @ =gPlayerEntity - movs r2, #0x2e - ldrsh r0, [r1, r2] - movs r3, #0x32 - ldrsh r1, [r1, r3] - ldrh r2, [r5] - ldrh r3, [r5, #2] - bl sub_080041E8 - lsrs r0, r0, #4 - mov r4, r8 - adds r4, #0x69 - mov r3, r8 - adds r3, #0x38 - movs r1, #0x58 - add r1, r8 - mov ip, r1 - cmp r0, #0x18 - ble _080684A8 - mov r2, r8 - ldr r5, [r2, #0x64] - adds r5, #0x90 - movs r2, #0x12 -_08068494: - ldr r0, [r5] - ldr r1, [r5, #4] - str r0, [r5, #8] - str r1, [r5, #0xc] - subs r5, #8 - subs r2, #1 - cmp r2, #0 - bge _08068494 - movs r0, #4 - mov sb, r0 -_080684A8: - mov r1, r8 - ldr r5, [r1, #0x64] - adds r5, #0x98 - ldrh r0, [r5] - strh r0, [r1, #0x2e] - ldrh r0, [r5, #2] - strh r0, [r1, #0x32] - ldrh r0, [r5, #4] - strh r0, [r1, #0x36] - ldrb r0, [r5, #7] - lsls r0, r0, #0x1a - lsrs r0, r0, #0x1a - strb r0, [r1, #0x14] - ldrb r0, [r5, #7] - lsrs r0, r0, #6 - strb r0, [r3] - ldrb r0, [r5, #6] - cmp r0, #0x16 - beq _080684D2 - cmp r0, #0xa - bne _080684DE -_080684D2: - mov r2, r8 - ldrb r1, [r2, #0x18] - movs r0, #4 - rsbs r0, r0, #0 - ands r0, r1 - strb r0, [r2, #0x18] -_080684DE: - adds r1, r4, #0 - ldrb r2, [r1] - movs r0, #0 - ldrsb r0, [r1, r0] - cmp r0, #0 - ble _080684EE - subs r0, r2, #1 - strb r0, [r1] -_080684EE: - mov r2, sb - mov r3, r8 - ldrb r0, [r3, #0x14] - lsrs r0, r0, #1 - add sb, r0 - mov r0, ip - ldrb r0, [r0] - cmp sb, r0 - beq _08068520 - cmp r2, #0 - bne _0806850C - movs r0, #0 - ldrsb r0, [r1, r0] - cmp r0, #0 - bgt _08068520 -_0806850C: - mov r0, r8 - mov r1, sb - bl InitAnimationForceUpdate - movs r0, #0x1e - strb r0, [r4] - b _08068526 - .align 2, 0 -_0806851C: .4byte gPlayerEntity -_08068520: - mov r0, r8 - bl UpdateAnimationSingleFrame -_08068526: - mov r0, r8 - bl sub_0800451C - mov r1, r8 - movs r2, #0x36 - ldrsh r0, [r1, r2] - cmp r0, #0 - bge _08068542 - movs r2, #0xc - rsbs r2, r2, #0 - mov r0, r8 - movs r1, #0 - bl sub_0806F854 -_08068542: - pop {r3, r4} - mov r8, r3 - mov sb, r4 - pop {r4, r5, r6, r7, pc} - .align 2, 0 - .syntax divided diff --git a/data/animations/npc/sturgeon.s b/data/animations/npc/sturgeon.s index a4fa9b14..dc4be7fd 100644 --- a/data/animations/npc/sturgeon.s +++ b/data/animations/npc/sturgeon.s @@ -45,4 +45,4 @@ gSpriteAnimations_Sturgeon:: @ 0810FBD0 .4byte gSpriteAnimations_Sturgeon_7 .4byte gSpriteAnimations_Sturgeon_8 .4byte gSpriteAnimations_Sturgeon_9 - .4byte 00000000 \ No newline at end of file + .4byte 00000000 diff --git a/include/flags.h b/include/flags.h index 0d5c331a..aceb9f9a 100644 --- a/include/flags.h +++ b/include/flags.h @@ -3,6 +3,8 @@ #include "global.h" +// clang-format off + bool32 CheckFlags(u32); bool32 CheckGlobalFlag(u32); bool32 CheckGlobalFlags(u32, u32); @@ -1762,4 +1764,6 @@ typedef enum { END_12, } LocalFlags12; +// clang-format on + #endif // FLAGS_H diff --git a/linker.ld b/linker.ld index 479e7328..d6e46ee3 100644 --- a/linker.ld +++ b/linker.ld @@ -867,7 +867,7 @@ SECTIONS { src/gba/m4a.o(.text); asm/lib/libagbsyscall.o(.text); src/eeprom.o(.text); - *libc.a:memcpy.o(.text); + libc.a(.text); /* handwritten assembly in arm mode */ asm/src/intr.o(.text); diff --git a/progress.py b/progress.py index bd248d2e..f0b8fdf2 100755 --- a/progress.py +++ b/progress.py @@ -2,7 +2,6 @@ import argparse import json -import git import os import re @@ -26,7 +25,7 @@ def parse_map(non_matching_funcs): data = 0 non_matching = 0 - with open('tmc.map', 'r') as map: + with open('build/USA/tmc.map', 'r') as map: # Skip to the linker script section line = map.readline() while not line.startswith('Linker script and memory map'): @@ -119,6 +118,7 @@ def main(): if args.format == 'csv': + import git version = 2 git_object = git.Repo().head.object timestamp = str(git_object.committed_date) diff --git a/src/npc/kid.c b/src/npc/kid.c index fe03c2ef..685d7da8 100644 --- a/src/npc/kid.c +++ b/src/npc/kid.c @@ -76,6 +76,7 @@ void sub_080620F4(Entity*); void sub_08062130(Entity*); void sub_08062194(Entity*); void (*const gUnk_0810BE0C[])(Entity*) = { sub_080620F4, sub_08062130, sub_08062194 }; +extern s32 sub_080041E8(s32 x1, s32 y1, s32 x2, s32 y2); void sub_08062194(Entity*); const Dialog gUnk_0810BE10[] = { @@ -182,17 +183,21 @@ void sub_080622F4(Entity*); void sub_0806265C(Entity*, ScriptExecutionContext*); void sub_0806252C(Entity*); -typedef struct { - u16 x; - u16 y; - u16 z; - u8 framestate; - u8 animationState : 6; - u8 collisionLayer : 2; +typedef union { + struct { + s16 x; + s16 y; + s16 z; + u8 framestate; + u8 animationState : 6; + u8 collisionLayer : 2; + } FIELDS; + u64 DWORD; } KidHeapItem; #define KID_HEAP_COUNT 0x14 typedef KidHeapItem KidHeap[KID_HEAP_COUNT]; +#define KID_HEAP ((KidHeapItem*)this->myHeap) void Kid(Entity* this) { if ((this->flags & ENT_SCRIPTED) != 0) { @@ -307,7 +312,107 @@ void sub_080621AC(Entity* this) { } } -ASM_FUNC("asm/non_matching/kid/sub_080622F4.inc", void sub_080622F4(Entity* this)) +#define KID_HEAP_SHIFT_RIGHT \ + heapPtr = KID_HEAP; \ + heapPtr += (KID_HEAP_COUNT - 2); \ + for (i = 0; i < (KID_HEAP_COUNT - 1); i++) { \ + heapPtr[1] = heapPtr[0]; \ + heapPtr--; \ + } + +void sub_080622F4(Entity* this) { + s32 dx; + s32 dy; + s32 dist; + s32 i; + + u32 animIndex; // used as 2nd param of InitAnimationForceUpdate + u32 animIndexTmp; + + KidHeapItem* heapPtr; + KidHeapItem item; + + // Prepended heap item is initialized from player's current state. + item.FIELDS.x = gPlayerEntity.x.HALF_U.HI; + item.FIELDS.y = gPlayerEntity.y.HALF_U.HI; + item.FIELDS.z = gPlayerEntity.z.HALF_U.HI; + item.FIELDS.framestate = gPlayerState.framestate; + item.FIELDS.animationState = gPlayerEntity.animationState; + item.FIELDS.collisionLayer = gPlayerEntity.collisionLayer; + + heapPtr = this->myHeap; + if (heapPtr->FIELDS.framestate == 0x16 && item.FIELDS.framestate != 0x16) { + dx = this->x.HALF.HI - gPlayerEntity.x.HALF.HI; + dy = this->y.HALF.HI - gPlayerEntity.y.HALF.HI; + + if (dx < 0) + dx = -dx; + if (dy < 0) + dy = -dy; + + if (dx > 120 || dy > 80) { + this->field_0x68.HALF.LO = 0; + return; + } + + sub_0806252C(this); + } + + animIndex = 0; + + if (item.DWORD != heapPtr->DWORD) { + KID_HEAP_SHIFT_RIGHT; + heapPtr = KID_HEAP; + heapPtr[0] = item; + + animIndex = 0x4; + if ((s8)this->field_0x68.HALF.HI > 0) { + this->field_0x68.HALF.HI = this->field_0x68.HALF.HI - 1; + } + } else { + heapPtr += KID_HEAP_COUNT - 1; + if (heapPtr->FIELDS.z < 0) { + KID_HEAP_SHIFT_RIGHT; + animIndex = 0x4; + } else { + dist = sub_080041E8(gPlayerEntity.x.HALF.HI, gPlayerEntity.y.HALF.HI, (u16)heapPtr->FIELDS.x, + (u16)heapPtr->FIELDS.y); + dist = ((u32)dist) >> 0x4; + if (dist > 0x18) { + + KID_HEAP_SHIFT_RIGHT; + animIndex = 0x4; + } + } + } + heapPtr = KID_HEAP; + heapPtr += +KID_HEAP_COUNT - 1; + this->x.HALF.HI = heapPtr->FIELDS.x; + this->y.HALF.HI = heapPtr->FIELDS.y; + this->z.HALF.HI = heapPtr->FIELDS.z; + this->animationState = heapPtr->FIELDS.animationState; + this->collisionLayer = heapPtr->FIELDS.collisionLayer; + + if (((s8)this->field_0x68.HALF.HI) > 0) { + this->field_0x68.HALF.HI = this->field_0x68.HALF.HI - 1; + } + + animIndexTmp = animIndex; + animIndex += this->animationState >> 1; + if (this->type == OBJECT) { + animIndex += 0x10; + } + + if (!(animIndex == this->animIndex || (animIndexTmp == 0 && ((s8)this->field_0x68.HALF.HI) > 0))) { + InitAnimationForceUpdate(this, animIndex); + this->field_0x68.HALF.HI = 0x1e; + } else { + UpdateAnimationSingleFrame(this); + } + + sub_0800451C(this); + return; +} void sub_08062500(Entity* this) { this->myHeap = zMalloc(sizeof(KidHeap)); @@ -388,12 +493,12 @@ void sub_0806252C(Entity* this) { x = 0; for (loopVar = KID_HEAP_COUNT - 1; loopVar >= 0; loopVar--) { - item->x = r5 - (x >> 8); - item->y = (r5 >> 0x10) - (y >> 8); - item->z = r6; - item->framestate = r6 >> 0x10; - item->animationState = this->animationState & 0x3f; - item->collisionLayer = this->collisionLayer; + item->FIELDS.x = r5 - (x >> 8); + item->FIELDS.y = (r5 >> 0x10) - (y >> 8); + item->FIELDS.z = r6; + item->FIELDS.framestate = r6 >> 0x10; + item->FIELDS.animationState = this->animationState & 0x3f; + item->FIELDS.collisionLayer = this->collisionLayer; item++; y = y + r8; x = x + r10; diff --git a/src/npc/zeldaFollower.c b/src/npc/zeldaFollower.c index d92e6018..e9c772a6 100644 --- a/src/npc/zeldaFollower.c +++ b/src/npc/zeldaFollower.c @@ -2,16 +2,36 @@ #include "entity.h" #include "functions.h" -typedef struct { - u16 x; - u16 y; - u16 z; - u8 framestate; - u8 animationState : 6; - u8 collisionLayer : 2; +extern s32 sub_080041E8(s32 x1, s32 y1, s32 x2, s32 y2); + +typedef union { + struct { + u16 x; + u16 y; + u16 z; + u8 framestate; + u8 animationState : 6; + u8 collisionLayer : 2; + } FIELDS; + u64 DWORD; } ZeldaFollowerItem; -typedef ZeldaFollowerItem ZeldaFollowerHeap[20]; +#define ZELDA_FOLLOWER_HEAP_LEN 20 + +#define ZELDA_FOLLOWER_HEAP ((ZeldaFollowerItem *)this->myHeap) +#define ZELDA_FOLLOWER_HEAP_END ((ZeldaFollowerItem *)this->myHeap + (ZELDA_FOLLOWER_HEAP_LEN - 1)) + +#define ZELDA_FOLLOWER_HEAP_SHIFT_RIGHT(this, heapPtr) \ + do { \ + int i; \ + heapPtr = ((ZeldaFollowerItem*)this->myHeap); \ + heapPtr += (ZELDA_FOLLOWER_HEAP_LEN - 2); \ + for ( i = 0; i < (ZELDA_FOLLOWER_HEAP_LEN - 1); i++) { \ + heapPtr[1] = heapPtr[0]; \ + heapPtr--; \ + } \ + } while (0) + void sub_08068318(Entity*); void sub_0806854C(Entity*, u32*); @@ -35,10 +55,99 @@ void ZeldaFollower(Entity* this) { } } -ASM_FUNC("asm/non_matching/zeldaFollower/sub_08068318.inc", void sub_08068318(Entity* this)) +void sub_08068318(Entity* this) { + s32 dist; + s16 z; + + u32 animIndex; + u32 animIndexTmp; + + ZeldaFollowerItem* heapPtr; + ZeldaFollowerItem item; + + item.FIELDS.x = gPlayerEntity.x.HALF_U.HI; + item.FIELDS.y = gPlayerEntity.y.HALF_U.HI; + item.FIELDS.z = gPlayerEntity.z.HALF_U.HI; + item.FIELDS.framestate = gPlayerState.framestate; + item.FIELDS.animationState = gPlayerEntity.animationState; + item.FIELDS.collisionLayer = gPlayerEntity.collisionLayer; + + heapPtr = this->myHeap; + + if ( (heapPtr->FIELDS.framestate == 0xa && item.FIELDS.framestate != 0xa) || + (heapPtr->FIELDS.framestate == 0x16 && item.FIELDS.framestate != 0x16)) { + this->x.HALF.HI = gPlayerEntity.x.HALF.HI; + this->y.HALF.HI = gPlayerEntity.y.HALF.HI; + this->spriteSettings.draw = 1; + sub_08068578(this); + } + + animIndex = 0; + if (item.DWORD != heapPtr->DWORD || item.FIELDS.framestate == 0x16 || item.FIELDS.framestate == 0xa ) { + ZELDA_FOLLOWER_HEAP_SHIFT_RIGHT(this, heapPtr); + heapPtr = ZELDA_FOLLOWER_HEAP; + heapPtr[0] = item; + animIndex = 0x4; + + if ((s8)this->field_0x68.HALF.HI > 0) { + this->field_0x68.HALF.HI = this->field_0x68.HALF.HI - 1; + } + } else { + heapPtr += ZELDA_FOLLOWER_HEAP_LEN - 1; + z = heapPtr->FIELDS.z; + + if (z < 0) { + ZELDA_FOLLOWER_HEAP_SHIFT_RIGHT(this, heapPtr); + animIndex = 0x4; + } else { + dist = sub_080041E8( + gPlayerEntity.x.HALF.HI, + gPlayerEntity.y.HALF.HI, + (u16) heapPtr->FIELDS.x, + (u16) heapPtr->FIELDS.y + ); + dist = ((u32)dist) >> 0x4; + if (dist > 0x18) { + ZELDA_FOLLOWER_HEAP_SHIFT_RIGHT(this, heapPtr); + animIndex = 0x4; + } + } + } + + heapPtr = ZELDA_FOLLOWER_HEAP; + heapPtr += ZELDA_FOLLOWER_HEAP_LEN - 1; + this->x.HALF.HI = heapPtr->FIELDS.x; + this->y.HALF.HI = heapPtr->FIELDS.y; + this->z.HALF.HI = heapPtr->FIELDS.z; + this->animationState = heapPtr->FIELDS.animationState; + this->collisionLayer = heapPtr->FIELDS.collisionLayer; + + if (heapPtr->FIELDS.framestate == 0x16 || heapPtr->FIELDS.framestate == 0xa) { + this->spriteSettings.draw = 0; + } + + if (((s8)this->field_0x68.HALF.HI) > 0) { + this->field_0x68.HALF.HI = this->field_0x68.HALF.HI - 1; + } + + animIndexTmp = animIndex; + animIndex += this->animationState >> 1; + + if (!(animIndex == this->animIndex || (animIndexTmp == 0 && ((s8)this->field_0x68.HALF.HI) > 0))) { + InitAnimationForceUpdate(this, animIndex); + this->field_0x68.HALF.HI = 0x1e; + } else { + UpdateAnimationSingleFrame(this); + } + + sub_0800451C(this); + if (this->z.HALF.HI < 0) { + sub_0806F854(this, 0x0, -0xc); + } +} void sub_0806854C(Entity* this, u32* none) { - this->myHeap = zMalloc(sizeof(ZeldaFollowerHeap)); + this->myHeap = zMalloc(sizeof(ZeldaFollowerItem[ZELDA_FOLLOWER_HEAP_LEN])); if (this->myHeap != NULL) { this->field_0x68.HALF.LO = 1; RemoveInteractableObject(this); @@ -156,12 +265,12 @@ void sub_08068578(Entity* this) { // Down here the u32 are suddendly accessed correctly as u16 and bitfields? // How are the results of above u32 calculations used? for (index = 0x13; index >= 0; index--) { - item->x = r5 - (x >> 8); - item->y = (r5 >> 0x10) - (y >> 8); - item->z = r6; - item->framestate = r6 >> 0x10; - item->animationState = this->animationState & 0x3f; - item->collisionLayer = this->collisionLayer; + item->FIELDS.x = r5 - (x >> 8); + item->FIELDS.y = (r5 >> 0x10) - (y >> 8); + item->FIELDS.z = r6; + item->FIELDS.framestate = r6 >> 0x10; + item->FIELDS.animationState = this->animationState & 0x3f; + item->FIELDS.collisionLayer = this->collisionLayer; item++; y = y + r8; x = x + r10; diff --git a/src/projectile/gyorgTail.c b/src/projectile/gyorgTail.c index c3657692..1f191f7a 100644 --- a/src/projectile/gyorgTail.c +++ b/src/projectile/gyorgTail.c @@ -169,70 +169,76 @@ void sub_080AC560(Entity* this) { this->y.WORD = entity->y.WORD - gSineTable[entity->direction + 0x40] * factor; } -NONMATCH("asm/non_matching/gyorgTail/sub_080AC5E4.inc", bool32 sub_080AC5E4(Entity* this)) { +bool32 sub_080AC5E4(Entity* this) { // TODO regalloc Entity* entity; + Entity* new_var2; Entity* entity2; Entity* entity3; Entity* entity4; - u8 uVar3; if (this->type == 0) { - if (0x43 < gEntCount) { + if (gEntCount > 0x43) { return FALSE; } this->field_0x78.HALF.HI = 0x11; - entity = CreateProjectile(0x22); + entity = CreateProjectile(GYORG_TAIL); entity->type = this->type; entity->type2 = 1; entity->parent = this->parent; entity->field_0x78.HALF.HI = 0x12; this->child = entity; - entity2 = CreateProjectile(0x22); + + entity2 = CreateProjectile(GYORG_TAIL); entity2->type = this->type; entity2->type2 = 2; entity2->parent = this->parent; entity2->field_0x78.HALF.HI = 0x14; - uVar3 = entity2->field_0x78.HALF.HI; - entity2->field_0x78.HALF.HI = uVar3; entity->child = entity2; - entity3 = CreateProjectile(0x22); + + entity3 = CreateProjectile(GYORG_TAIL); entity3->type = this->type; entity3->type2 = 3; entity3->parent = this->parent; entity3->child = NULL; entity3->field_0x78.HALF.HI = 0; entity2->child = entity3; + + entity = CreateProjectile(GYORG_TAIL); + entity->type = this->type; + entity->type2 = 4; + entity->parent = this->parent; + entity->child = this; + entity->field_0x78.HALF.HI = 0x14; } else { - if (0x44 < gEntCount) { + if (gEntCount > 0x44) { return FALSE; } this->field_0x78.HALF.HI = 0xf; - entity = CreateProjectile(0x22); + entity = CreateProjectile(GYORG_TAIL); entity->type = this->type; entity->type2 = 1; entity->parent = this->parent; entity->field_0x78.HALF.HI = 0x10; - uVar3 = entity->field_0x78.HALF.HI; - entity->field_0x78.HALF.HI = uVar3; this->child = entity; - entity2 = CreateProjectile(0x22); + + entity2 = CreateProjectile(GYORG_TAIL); entity2->type = this->type; entity2->type2 = 2; entity2->parent = this->parent; entity2->child = NULL; entity2->field_0x78.HALF.HI = 0x20; entity->child = entity2; + + entity = CreateProjectile(GYORG_TAIL); + entity->type = this->type; + entity->type2 = 4; + entity->parent = this->parent; + entity->child = this; + entity->field_0x78.HALF.HI = 0x10; } - entity = CreateProjectile(0x22); - entity->type = this->type; - entity->type2 = 4; - entity->parent = this->parent; - entity->child = this; - entity->field_0x78.HALF.HI = uVar3; return TRUE; } -END_NONMATCH void sub_080AC6F0(Entity* this) { static const u8 gUnk_0812A9C0[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, diff --git a/tools/.gitignore b/tools/.gitignore index 633ffe46..e305c308 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -1,2 +1,4 @@ bin cmake-build* +*.cmake +json*.hpp \ No newline at end of file diff --git a/tools/src/scaninc/asm_file.cpp b/tools/src/scaninc/asm_file.cpp index f53777bc..18b1e956 100644 --- a/tools/src/scaninc/asm_file.cpp +++ b/tools/src/scaninc/asm_file.cpp @@ -29,7 +29,7 @@ AsmFile::AsmFile(std::string path) { FILE* fp = std::fopen(path.c_str(), "rb"); if (fp == NULL) - fatal_error("Failed to open \"%s\" for reading.\n", path.c_str()); + fatal_error("Failed to open \"{}\" for reading.\n", path.c_str()); std::fseek(fp, 0, SEEK_END); @@ -40,7 +40,7 @@ AsmFile::AsmFile(std::string path) { std::rewind(fp); if (std::fread(m_buffer, m_size, 1, fp) != 1) - fatal_error("Failed to read \"%s\".\n", path.c_str()); + fatal_error("Failed to read \"{}\".\n", path.c_str()); std::fclose(fp); @@ -159,7 +159,7 @@ void AsmFile::SkipString() { fatal_error(INPUT_ERROR_MESSAGE("unexpected EOF in string\n")); if (c == '\\') { - c = GetChar(); + c = GetChar(); } } } diff --git a/tools/src/scaninc/c_file.cpp b/tools/src/scaninc/c_file.cpp index a8ff921f..fc5ed555 100644 --- a/tools/src/scaninc/c_file.cpp +++ b/tools/src/scaninc/c_file.cpp @@ -26,7 +26,7 @@ CFile::CFile(std::string path) { FILE* fp = std::fopen(path.c_str(), "rb"); if (fp == NULL) - fatal_error("Failed to open \"%s\" for reading.\n", path.c_str()); + fatal_error("Failed to open \"{}\" for reading.\n", path.c_str()); std::fseek(fp, 0, SEEK_END); @@ -38,7 +38,7 @@ CFile::CFile(std::string path) { std::rewind(fp); if (std::fread(m_buffer, m_size, 1, fp) != 1) - fatal_error("Failed to read \"%s\".\n", path.c_str()); + fatal_error("Failed to read \"{}\".\n", path.c_str()); std::fclose(fp); diff --git a/tools/src/scaninc/source_file.cpp b/tools/src/scaninc/source_file.cpp index 0cefc9f1..107396b0 100644 --- a/tools/src/scaninc/source_file.cpp +++ b/tools/src/scaninc/source_file.cpp @@ -25,7 +25,7 @@ SourceFileType GetFileType(std::string& path) { std::size_t pos = path.find_last_of('.'); if (pos == std::string::npos) - fatal_error("no file extension in path \"%s\"\n", path.c_str()); + fatal_error("no file extension in path \"{}\"\n", path.c_str()); std::string extension = path.substr(pos + 1); @@ -38,7 +38,7 @@ SourceFileType GetFileType(std::string& path) { else if (extension == "inc") return SourceFileType::Inc; else - fatal_error("Unrecognized extension \"%s\"\n", extension.c_str()); + fatal_error("Unrecognized extension \"{}\"\n", extension.c_str()); // Unreachable return SourceFileType::Cpp;